Come rimuovo i duplicati da un array C #?


209

Ho lavorato con un string[]array in C # che viene restituito da una chiamata di funzione. Potrei probabilmente lanciare una Genericraccolta, ma mi chiedevo se ci fosse un modo migliore per farlo, possibilmente usando un array temporaneo.

Qual è il modo migliore per rimuovere i duplicati da un array C #?


4
Utilizzare il metodo di estensione Distinct.
kokos,

Infatti. È più divertente quando l'array è già ordinato, in tal caso può essere eseguito sul posto nel tempo O (n).
David Airapetyan,

@ Vitim.us No. Nel mio caso, non è nemmeno un array, ma un Elenco <stringa>. Accetto qualsiasi risposta che faccia il lavoro. Forse è uno shock doverlo fare sulla carta.
AngryHacker il

Risposte:


427

È possibile utilizzare una query LINQ per fare ciò:

int[] s = { 1, 2, 3, 3, 4};
int[] q = s.Distinct().ToArray();

22
Si noti che è possibile utilizzare un IEqualityComparer come parametro, ad esempio .Distinct(StringComparer.OrdinalIgnoreCase)per ottenere una serie distinta di stringhe senza distinzione tra maiuscole e minuscole.
justisb,

Distinct onora l'ordine originale degli elementi?
asyrov,

@asyrov: da MSDN:The Distinct() method returns an unordered sequence that contains no duplicate values.
tigrou,

52

Ecco l' approccio HashSet <string> :

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

Sfortunatamente questa soluzione richiede anche .NET framework 3.5 o successivo poiché HashSet non è stato aggiunto fino a quella versione. È inoltre possibile utilizzare array.Distinct () , che è una funzionalità di LINQ.


11
Questo probabilmente non preserverà l'ordine originale.
Hamish Grubijan,

11

Il seguente codice testato e funzionante rimuoverà i duplicati da un array. È necessario includere lo spazio dei nomi 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]);
}

Potresti concludere questo in una funzione se lo desideri.


Questo sembra essere O (N ^ 2) ... Potresti usare un heap invece di una ArrayList
Neil Chowdhury

10

Se fosse necessario ordinarlo, è possibile implementare un ordinamento che rimuove anche i duplicati.

Uccide quindi due piccioni con una fava.


7
In che modo l'ordinamento rimuove i duplicati?
dan

2
Chi l'ha votato? Questa non è una risposta "Come faccio i pancake?" "Metti alcuni ingredienti in un arco e mescola."
Quarkly

9

Questo potrebbe dipendere da quanto vuoi progettare la soluzione - se l'array non sarà mai così grande e non ti interessa ordinare l'elenco potresti provare qualcosa di simile al seguente:

    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));
    }

4
È necessario utilizzare Elenco anziché ArrayList.
Doug S,

7

- Questa è la domanda di intervista posta ogni volta. Ora ho fatto la sua codifica.

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();
        }

3
Non dovresti fare una complessità temporale O (n * 2) per una domanda del genere.
dan

2
Dovresti usare Merge sort
Nick Gallimore il

7
List<String> myStringList = new List<string>();
foreach (string s in myStringArray)
{
    if (!myStringList.Contains(s))
    {
        myStringList.Add(s);
    }
}

Questo è O (n ^ 2) , che non importa per un breve elenco che verrà inserito in una combo, ma potrebbe essere rapidamente un problema in una grande raccolta.


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 ");
    }
}

6

Ecco un approccio O (n * n) che utilizza lo spazio 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';
    }
}

Gli approcci hash / linq sopra sono quelli che useresti generalmente nella vita reale. Tuttavia, nelle interviste di solito vogliono mettere alcuni vincoli, ad esempio lo spazio costante che esclude l'hash o nessuna API interna , che esclude l'utilizzo di LINQ .


1
Come può mai usare lo spazio O (1), quando è necessario memorizzare l'intero elenco? Iniziando con un ordinamento sul posto, puoi fare O (nlogn) tempo e O (n) memoria, con molto meno codice.
Thomas Ahle,

1
Cosa ti fa pensare che stia memorizzando l'intero elenco? Sta davvero facendo sul posto. E sebbene non sia una condizione nella domanda, il mio codice mantiene l'ordine dei caratteri nella stringa originale. L'ordinamento lo rimuoverà.
Sesh,

1
Il ciclo interno ( strIn[j] == strIn[i]) confronterà una stringa con se stesso, a meno che non sia spiegata con un'istruzione if.
Utente3219

5

Aggiungi tutte le stringhe a un dizionario e ottieni successivamente la proprietà Keys. Questo produrrà ogni stringa univoca, ma non necessariamente nello stesso ordine in cui erano stati inseriti dall'input originale.

Se si richiede che il risultato finale abbia lo stesso ordine dell'input originale, quando si considera la prima occorrenza di ogni stringa, utilizzare invece il seguente algoritmo:

  1. Avere un elenco (output finale) e un dizionario (per verificare la presenza di duplicati)
  2. Per ogni stringa nell'input, controlla se esiste già nel dizionario
  3. In caso contrario, aggiungilo sia al dizionario che all'elenco

Alla fine, l'elenco contiene la prima occorrenza di ogni stringa univoca.

Assicurati di considerare cose come la cultura e simili quando costruisci il tuo dizionario, per assicurarti di gestire correttamente i duplicati con lettere accentate.


5

Il seguente pezzo di codice tenta di rimuovere i duplicati da una ArrayList sebbene questa non sia una soluzione ottimale. Mi è stata posta questa domanda durante un'intervista per rimuovere i duplicati attraverso la ricorsione, e senza usare un secondo / temporaneo arraylist:

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);
            }
        }

5

Soluzione semplice:

using System.Linq;
...

public static int[] Distinct(int[] handles)
{
    return handles.ToList().Distinct().ToArray();
}

5

Forse hashset che non memorizza elementi duplicati e ignora silenziosamente le richieste di aggiunta di duplicati.

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 + ",");
}

4

NOTA: NON testato!

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();
}

Potrebbe fare quello che ti serve ...

EDIT Argh !!! battuto ad esso da rob da meno di un minuto!


Rob non ti ha battuto per niente. Sta usando ArrayList, mentre stai usando Elenco. La tua versione è migliore.
Doug S,

4

Testato il seguito e funziona. La cosa bella è che fa anche una ricerca sensibile alla cultura

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


4

Questo codice rimuove al 100% i valori duplicati da un array [come ho usato un [i]] ..... Puoi convertirlo in qualsiasi linguaggio 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--;
        }
    }

}

4

Metodo di estensione generico:

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;
        }
    }
}

1

puoi usare questo codice quando lavori con un 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]);

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;
}

0

Di seguito è riportata una semplice logica in Java che attraversi due volte gli elementi dell'array e se vedi lo stesso elemento a cui assegni zero e non tocchi l'indice dell'elemento che stai confrontando.

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;
            }
        }
    }
}

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;
        }

1
spiega la tua risposta, per favore.
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;
         }
    }



}

Questo è un modo molto inefficiente per farlo. Dai un'occhiata alle altre risposte per vedere cosa fanno.
Wai Ha Lee,

0
strINvalues = "1,1,2,2,3,3,4,4";
strINvalues = string.Join(",", strINvalues .Split(',').Distinct().ToArray());
Debug.Writeline(strINvalues);

Kkk Non sono sicuro che si tratti di stregoneria o di un bellissimo codice

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

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

1 Suddivisione dell'array e utilizzo di Distinct [LINQ] per rimuovere i duplicati 2 Unire nuovamente senza i duplicati.

Mi dispiace non ho mai letto il testo su StackOverFlow solo il codice. ha più senso del testo;)


Le risposte di solo codice sono risposte di bassa qualità. Aggiungi una spiegazione al perché funziona.
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--;
                }
            }
        }

1
Benvenuti in SO. Sebbene questo frammento di codice possa essere la soluzione, includere una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che in futuro stai rispondendo alla domanda dei lettori e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
alan.elkin,

Purtroppo questo codice non rimuove nulla, quindi non rimuove i duplicati.
P_P,

0

Il modo migliore? Difficile dirlo, l'approccio di HashSet sembra veloce, ma (a seconda dei dati) l'utilizzo di un algoritmo di ordinamento (CountSort?) Può essere molto più veloce.

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;
    }
}

Quasi senza rami. Come? Modalità debug, Step Into (F11) con un piccolo array: {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;
    }

Una soluzione con due loop nidificati potrebbe richiedere del tempo, soprattutto per array più grandi.

    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;
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.