Jak usunąć duplikaty z tablicy C #?

209

Pracowałem z string[]tablicą w języku C #, która jest zwracana z wywołania funkcji. Mogłem ewentualnie przesyłać do Generickolekcji, ale zastanawiałem się, czy istnieje lepszy sposób, aby to zrobić, być może przy użyciu tablicy tymczasowej.

Jaki jest najlepszy sposób na usunięcie duplikatów z tablicy C #?

lomaxx
źródło
4
Użyj metody rozszerzenia Distinct.
kokos
W rzeczy samej. Więcej zabawy sprawia, że ​​tablica jest już posortowana - w takim przypadku można to zrobić na miejscu w czasie O (n).
David Airapetyan
@ Vitim.us Nope. W moim przypadku nie jest to nawet tablica, ale lista <ciąg>. Przyjmuję każdą odpowiedź, która działa. Być może to szok, że trzeba to zrobić na papierze.
AngryHacker

Odpowiedzi:

427

Aby to zrobić, możesz użyć zapytania LINQ:

int[] s = { 1, 2, 3, 3, 4};
int[] q = s.Distinct().ToArray();
Jeff Atwood
źródło
22
Zauważ, że możesz użyć IEqualityComparer jako parametru, na przykład, .Distinct(StringComparer.OrdinalIgnoreCase)aby otrzymać rozróżniany bez rozróżniania wielkości liter zestaw ciągów.
justisb
Czy Distinct honoruje oryginalną kolejność elementów?
asyrov
@asyrov: z MSDN:The Distinct() method returns an unordered sequence that contains no duplicate values.
tigrou
52

Oto podejście HashSet <ciąg> :

public static string[] RemoveDuplicates(string[] s)
{
    HashSet<string> set = new HashSet<string>(s);
    string[] result = new string[set.Count];
    set.CopyTo(result);
    return result;
}

Niestety to rozwiązanie wymaga również .NET Framework 3.5 lub nowszego, ponieważ HashSet został dodany dopiero w tej wersji. Możesz także użyć array.Distinct () , która jest funkcją LINQ.

Arktur
źródło
11
To prawdopodobnie nie zachowa oryginalnego zamówienia.
Hamish Grubijan,
11

Poniższy przetestowany i działający kod usunie duplikaty z tablicy. Musisz dołączyć przestrzeń nazw System.Collections.

string[] sArray = {"a", "b", "b", "c", "c", "d", "e", "f", "f"};
var sList = new ArrayList();

for (int i = 0; i < sArray.Length; i++) {
    if (sList.Contains(sArray[i]) == false) {
        sList.Add(sArray[i]);
    }
}

var sNew = sList.ToArray();

for (int i = 0; i < sNew.Length; i++) {
    Console.Write(sNew[i]);
}

Możesz zawinąć to w funkcję, jeśli chcesz.

GateKiller
źródło
Wygląda na to, że to O (N ^ 2) ... Możesz użyć sterty zamiast ArrayList
Neil Chowdhury
10

Jeśli musisz to posortować, możesz zaimplementować sortowanie, które również usuwa duplikaty.

Zabija dwa ptaki jednym kamieniem.

Matthew Schinckel
źródło
7
W jaki sposób sortowanie usuwa duplikaty?
dan1
2
Kto to zagłosował? To nie jest odpowiedź. „Jak zrobić naleśniki?” „Połóż niektóre składniki w łuku i wymieszaj”.
Quarkly
9

Może to zależeć od tego, jak bardzo chcesz zaprojektować rozwiązanie - jeśli tablica nigdy nie będzie tak duża i nie obchodzi Cię sortowanie listy, możesz spróbować czegoś podobnego do następującego:

    public string[] RemoveDuplicates(string[] myList) {
        System.Collections.ArrayList newList = new System.Collections.ArrayList();

        foreach (string str in myList)
            if (!newList.Contains(str))
                newList.Add(str);
        return (string[])newList.ToArray(typeof(string));
    }
rjzii
źródło
4
Powinieneś użyć List zamiast ArrayList.
Doug S
7

- To pytanie do wywiadu zadawane za każdym razem. Teraz zrobiłem jego kodowanie.

static void Main(string[] args)
{    
            int[] array = new int[] { 4, 8, 4, 1, 1, 4, 8 };            
            int numDups = 0, prevIndex = 0;

            for (int i = 0; i < array.Length; i++)
            {
                bool foundDup = false;
                for (int j = 0; j < i; j++)
                {
                    if (array[i] == array[j])
                    {
                        foundDup = true;
                        numDups++; // Increment means Count for Duplicate found in array.
                        break;
                    }                    
                }

                if (foundDup == false)
                {
                    array[prevIndex] = array[i];
                    prevIndex++;
                }
            }

            // Just Duplicate records replce by zero.
            for (int k = 1; k <= numDups; k++)
            {               
                array[array.Length - k] = '\0';             
            }


            Console.WriteLine("Console program for Remove duplicates from array.");
            Console.Read();
        }
Muhammad Mubashir
źródło
3
Nie powinieneś robić złożoności czasowej O (n * 2) dla tego pytania.
dan1
2
Powinieneś użyć sortowania
korespondencji
7
List<String> myStringList = new List<string>();
foreach (string s in myStringArray)
{
    if (!myStringList.Contains(s))
    {
        myStringList.Add(s);
    }
}

To jest O (n ^ 2) , co nie będzie miało znaczenia dla krótkiej listy, która ma zostać upakowana w kombinację, ale może szybko stanowić problem w dużej kolekcji.

Will Dean
źródło
6
protected void Page_Load(object sender, EventArgs e)
{
    string a = "a;b;c;d;e;v";
    string[] b = a.Split(';');
    string[] c = b.Distinct().ToArray();

    if (b.Length != c.Length)
    {
        for (int i = 0; i < b.Length; i++)
        {
            try
            {
                if (b[i].ToString() != c[i].ToString())
                {
                    Response.Write("Found duplicate " + b[i].ToString());
                    return;
                }
            }
            catch (Exception ex)
            {
                Response.Write("Found duplicate " + b[i].ToString());
                return;
            }
        }              
    }
    else
    {
        Response.Write("No duplicate ");
    }
}
Pintu
źródło
6

Oto podejście O (n * n), które wykorzystuje przestrzeń O (1) .

void removeDuplicates(char* strIn)
{
    int numDups = 0, prevIndex = 0;
    if(NULL != strIn && *strIn != '\0')
    {
        int len = strlen(strIn);
        for(int i = 0; i < len; i++)
        {
            bool foundDup = false;
            for(int j = 0; j < i; j++)
            {
                if(strIn[j] == strIn[i])
                {
                    foundDup = true;
                    numDups++;
                    break;
                }
            }

            if(foundDup == false)
            {
                strIn[prevIndex] = strIn[i];
                prevIndex++;
            }
        }

        strIn[len-numDups] = '\0';
    }
}

Podejścia do hash / linq powyżej są tym, czego zwykle używasz w prawdziwym życiu. Jednak w wywiadach zwykle chcą nałożyć pewne ograniczenia, np. Stałą przestrzeń wykluczającą hasz lub brak wewnętrznego interfejsu API - co wyklucza użycie LINQ .

Sesh
źródło
1
Jak w ogóle może korzystać z miejsca O (1), skoro musisz przechowywać całą listę? Zaczynając od sortowania na miejscu, możesz wykonywać czas O (nlogn) i pamięć O (n) przy znacznie mniejszym kodzie.
Thomas Ahle
1
Co sprawia, że ​​myślisz, że przechowuje całą listę? Rzeczywiście działa w miejscu. I choć nie jest to warunek w pytaniu, mój kod zachowuje porządek znaków w oryginalnym ciągu. Sortowanie to usunie.
Sesh
1
Wewnętrzna pętla ( strIn[j] == strIn[i]) będzie porównywała ciąg do siebie, chyba że zostanie uwzględniona instrukcja if.
User3219,
5

Dodaj wszystkie ciągi do słownika, a następnie uzyskaj właściwość Keys. Spowoduje to wygenerowanie każdego unikalnego ciągu, ale niekoniecznie w tej samej kolejności, w jakiej znajdowały się w nim oryginalne dane wejściowe.

Jeśli chcesz, aby wynik końcowy miał taką samą kolejność jak oryginalne dane wejściowe, rozważając pierwsze wystąpienie każdego łańcucha, zastosuj następujący algorytm:

  1. Mieć listę (wynik końcowy) i słownik (w celu sprawdzenia duplikatów)
  2. Dla każdego ciągu wejściowego sprawdź, czy już istnieje w słowniku
  3. Jeśli nie, dodaj go zarówno do słownika, jak i do listy

Na końcu lista zawiera pierwsze wystąpienie każdego unikalnego ciągu.

Upewnij się, że podczas konstruowania słownika bierzesz pod uwagę takie elementy, jak kultura, i upewnij się, że poprawnie obsługujesz duplikaty z literami akcentowanymi.

Lasse V. Karlsen
źródło
5

Poniższy fragment kodu próbuje usunąć duplikaty z ArrayList, choć nie jest to optymalne rozwiązanie. Zadano mi to pytanie podczas wywiadu w celu usunięcia duplikatów poprzez rekurencję i bez użycia tablicy drugiego / tymczasowego:

private void RemoveDuplicate() 
{

ArrayList dataArray = new ArrayList(5);

            dataArray.Add("1");
            dataArray.Add("1");
            dataArray.Add("6");
            dataArray.Add("6");
            dataArray.Add("6");
            dataArray.Add("3");
            dataArray.Add("6");
            dataArray.Add("4");
            dataArray.Add("5");
            dataArray.Add("4");
            dataArray.Add("1");

            dataArray.Sort();

            GetDistinctArrayList(dataArray, 0);
}

private void GetDistinctArrayList(ArrayList arr, int idx)

{

            int count = 0;

            if (idx >= arr.Count) return;

            string val = arr[idx].ToString();
            foreach (String s in arr)
            {
                if (s.Equals(arr[idx]))
                {
                    count++;
                }
            }

            if (count > 1)
            {
                arr.Remove(val);
                GetDistinctArrayList(arr, idx);
            }
            else
            {
                idx += 1;
                GetDistinctArrayList(arr, idx);
            }
        }
Vijay Swami
źródło
5

Proste rozwiązanie:

using System.Linq;
...

public static int[] Distinct(int[] handles)
{
    return handles.ToList().Distinct().ToArray();
}
Fábio Delboni
źródło
5

Może hashset, który nie przechowuje duplikatów elementów i po cichu ignoruje żądania dodania duplikatów.

static void Main()
{
    string textWithDuplicates = "aaabbcccggg";     

    Console.WriteLine(textWithDuplicates.Count());  
    var letters = new HashSet<char>(textWithDuplicates);
    Console.WriteLine(letters.Count());

    foreach (char c in letters) Console.Write(c);
    Console.WriteLine("");

    int[] array = new int[] { 12, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 };

    Console.WriteLine(array.Count());
    var distinctArray = new HashSet<int>(array);
    Console.WriteLine(distinctArray.Count());

    foreach (int i in distinctArray) Console.Write(i + ",");
}
lukaszk
źródło
4

UWAGA: NIE testowane!

string[] test(string[] myStringArray)
{
    List<String> myStringList = new List<string>();
    foreach (string s in myStringArray)
    {
        if (!myStringList.Contains(s))
        {
            myStringList.Add(s);
        }
    }
    return myStringList.ToString();
}

Może zrobić to, czego potrzebujesz ...

EDYCJA Argh !!! pobity przez rob przez niecałą minutę!

ZombieSheep
źródło
Rob do niczego cię nie pobił. Używa ArrayList, podczas gdy ty używasz listy. Twoja wersja jest lepsza.
Doug S
4

Przetestowałem poniżej i działa. Fajne jest to, że przeprowadza wyszukiwanie wrażliwe na kulturę

class RemoveDuplicatesInString
{
    public static String RemoveDups(String origString)
    {
        String outString = null;
        int readIndex = 0;
        CompareInfo ci = CultureInfo.CurrentCulture.CompareInfo;


        if(String.IsNullOrEmpty(origString))
        {
            return outString;
        }

        foreach (var ch in origString)
        {
            if (readIndex == 0)
            {
                outString = String.Concat(ch);
                readIndex++;
                continue;
            }

            if (ci.IndexOf(origString, ch.ToString().ToLower(), 0, readIndex) == -1)
            {
                //Unique char as this char wasn't found earlier.
                outString = String.Concat(outString, ch);                   
            }

            readIndex++;

        }


        return outString;
    }


    static void Main(string[] args)
    {
        String inputString = "aAbcefc";
        String outputString;

        outputString = RemoveDups(inputString);

        Console.WriteLine(outputString);
    }

}

--AptSenSDET

AptSenSDET
źródło
4

Ten kod w 100% usuwa zduplikowane wartości z tablicy [jak użyłem [i]] ..... Możesz przekonwertować go na dowolny język OO ..... :)

for(int i=0;i<size;i++)
{
    for(int j=i+1;j<size;j++)
    {
        if(a[i] == a[j])
        {
            for(int k=j;k<size;k++)
            {
                 a[k]=a[k+1];
            }
            j--;
            size--;
        }
    }

}
Salman Ramzan
źródło
4

Ogólna metoda rozszerzenia:

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    HashSet<TSource> set = new HashSet<TSource>(comparer);
    foreach (TSource item in source)
    {
        if (set.Add(item))
        {
            yield return item;
        }
    }
}
Ali Bayat
źródło
1

możesz użyć tego kodu podczas pracy z ArrayList

ArrayList arrayList;
//Add some Members :)
arrayList.Add("ali");
arrayList.Add("hadi");
arrayList.Add("ali");

//Remove duplicates from array
  for (int i = 0; i < arrayList.Count; i++)
    {
       for (int j = i + 1; j < arrayList.Count ; j++)
           if (arrayList[i].ToString() == arrayList[j].ToString())
                 arrayList.Remove(arrayList[j]);
reza akhlaghi
źródło
1
public static int RemoveDuplicates(ref int[] array)
{
    int size = array.Length;

    // if 0 or 1, return 0 or 1:
    if (size  < 2) {
        return size;
    }

    int current = 0;
    for (int candidate = 1; candidate < size; ++candidate) {
        if (array[current] != array[candidate]) {
            array[++current] = array[candidate];
        }
    }

    // index to count conversion:
    return ++current;
}
Harry Martyrossian
źródło
0

Poniżej znajduje się prosta logika w java, w której dwukrotnie przemierzasz elementy tablicy, a jeśli widzisz ten sam element, przypisujesz mu zero, a także nie dotykasz indeksu elementu, który porównujesz.

import java.util.*;
class removeDuplicate{
int [] y ;

public removeDuplicate(int[] array){
    y=array;

    for(int b=0;b<y.length;b++){
        int temp = y[b];
        for(int v=0;v<y.length;v++){
            if( b!=v && temp==y[v]){
                y[v]=0;
            }
        }
    }
}
Papasani Mohansrinivas
źródło
0
  private static string[] distinct(string[] inputArray)
        {
            bool alreadyExists;
            string[] outputArray = new string[] {};

            for (int i = 0; i < inputArray.Length; i++)
            {
                alreadyExists = false;
                for (int j = 0; j < outputArray.Length; j++)
                {
                    if (inputArray[i] == outputArray[j])
                        alreadyExists = true;
                }
                        if (alreadyExists==false)
                        {
                            Array.Resize<string>(ref outputArray, outputArray.Length + 1);
                            outputArray[outputArray.Length-1] = inputArray[i];
                        }
            }
            return outputArray;
        }
Arie Yehieli
źródło
1
proszę wyjaśnij swoją odpowiedź.
Badiparmagi,
0
using System;
using System.Collections.Generic;
using System.Linq;


namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
             List<int> listofint1 = new List<int> { 4, 8, 4, 1, 1, 4, 8 };
           List<int> updatedlist= removeduplicate(listofint1);
            foreach(int num in updatedlist)
               Console.WriteLine(num);
        }


        public static List<int> removeduplicate(List<int> listofint)
         {
             List<int> listofintwithoutduplicate= new List<int>();


              foreach(var num in listofint)
                 {
                  if(!listofintwithoutduplicate.Any(p=>p==num))
                        {
                          listofintwithoutduplicate.Add(num);
                        }
                  }
             return listofintwithoutduplicate;
         }
    }



}
Rohan
źródło
Jest to bardzo nieefektywny sposób na zrobienie tego. Spójrz na inne odpowiedzi, aby zobaczyć, co robią.
Wai Ha Lee,
0
strINvalues = "1,1,2,2,3,3,4,4";
strINvalues = string.Join(",", strINvalues .Split(',').Distinct().ToArray());
Debug.Writeline(strINvalues);

Kkk Nie jestem pewien, czy to czary, czy tylko piękny kod

1 strINvalues ​​.Split (','). Distinct (). ToArray ()

2) string.Join (",", XXX);

1 Podział tablicy i użycie Distinct [LINQ] do usunięcia duplikatów 2 Ponowne połączenie z powrotem bez duplikatów.

Niestety, nigdy nie czytałem tekstu na StackOverFlow, tylko kod. ma to większy sens niż tekst;)

Kudakwashe Mafutah
źródło
Odpowiedzi tylko kodowe są odpowiedziami niskiej jakości. Dodaj wyjaśnienie, dlaczego to działa.
Taslim Oseni
0
int size = a.Length;
        for (int i = 0; i < size; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                if (a[i] == a[j])
                {
                    for (int k = j; k < size; k++)
                    {
                        if (k != size - 1)
                        {
                            int temp = a[k];
                            a[k] = a[k + 1];
                            a[k + 1] = temp;

                        }
                    }
                    j--;
                    size--;
                }
            }
        }
Swathi Sriramaneni
źródło
1
Witamy w SO. Ten fragment kodu może być rozwiązaniem, ale wyjaśnienie naprawdę pomaga poprawić jakość posta. Pamiętaj, że w przyszłości odpowiadasz na pytanie dla czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu.
alan.elkin
Niestety ten kod niczego nie usuwa, więc nie usuwa duplikatów.
P_P
0

Najlepszym sposobem? Trudno powiedzieć, podejście HashSet wygląda szybko, ale (w zależności od danych) użycie algorytmu sortowania (CountSort?) Może być znacznie szybsze.

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        Random r = new Random(0); int[] a, b = new int[1000000];
        for (int i = b.Length - 1; i >= 0; i--) b[i] = r.Next(b.Length);
        a = new int[b.Length]; Array.Copy(b, a, b.Length);
        a = dedup0(a); Console.WriteLine(a.Length);
        a = new int[b.Length]; Array.Copy(b, a, b.Length);
        var w = System.Diagnostics.Stopwatch.StartNew();
        a = dedup0(a); Console.WriteLine(w.Elapsed); Console.Read();
    }

    static int[] dedup0(int[] a)  // 48 ms  
    {
        return new HashSet<int>(a).ToArray();
    }

    static int[] dedup1(int[] a)  // 68 ms
    {
        Array.Sort(a); int i = 0, j = 1, k = a.Length; if (k < 2) return a;
        while (j < k) if (a[i] == a[j]) j++; else a[++i] = a[j++];
        Array.Resize(ref a, i + 1); return a;
    }

    static int[] dedup2(int[] a)  //  8 ms
    {
        var b = new byte[a.Length]; int c = 0;
        for (int i = 0; i < a.Length; i++) 
            if (b[a[i]] == 0) { b[a[i]] = 1; c++; }
        a = new int[c];
        for (int j = 0, i = 0; i < b.Length; i++) if (b[i] > 0) a[j++] = i;
        return a;
    }
}

Prawie bez oddziałów. W jaki sposób? Tryb debugowania, krok do (F11) z małą tablicą: {1,3,1,1,0}

    static int[] dedupf(int[] a)  //  4 ms
    {
        if (a.Length < 2) return a;
        var b = new byte[a.Length]; int c = 0, bi, ai, i, j;
        for (i = 0; i < a.Length; i++)
        { ai = a[i]; bi = 1 ^ b[ai]; b[ai] |= (byte)bi; c += bi; }
        a = new int[c]; i = 0; while (b[i] == 0) i++; a[0] = i++;
        for (j = 0; i < b.Length; i++) a[j += bi = b[i]] += bi * i; return a;
    }

Rozwiązanie z dwiema zagnieżdżonymi pętlami może zająć trochę czasu, szczególnie w przypadku większych tablic.

    static int[] dedup(int[] a)
    {
        int i, j, k = a.Length - 1;
        for (i = 0; i < k; i++)
            for (j = i + 1; j <= k; j++) if (a[i] == a[j]) a[j--] = a[k--];
        Array.Resize(ref a, k + 1); return a;
    }
P_P
źródło