Como faço para remover duplicatas de uma matriz C #?

209

Eu tenho trabalhado com um string[] matriz em c # que é retornada de uma chamada de função. Eu poderia transmitir para uma Genericcoleção, mas queria saber se havia uma maneira melhor de fazê-lo, possivelmente usando uma matriz temporária.

Qual é a melhor maneira de remover duplicatas de uma matriz C #?

lomaxx
fonte
4
Use o método de extensão Distinct.
Kokos
De fato. É mais divertido quando a matriz já está classificada - nesse caso, pode ser feita no local em O (n) tempo.
David Airapetyan
@ Vitim.us Não. No meu caso, nem sequer é uma matriz, mas uma Lista <string>. Eu aceito qualquer resposta que faça o trabalho. Talvez seja um choque ter que fazer isso no papel.
AngryHacker 23/11/12

Respostas:

427

Você poderia usar uma consulta LINQ para fazer isso:

int[] s = { 1, 2, 3, 3, 4};
int[] q = s.Distinct().ToArray();
Jeff Atwood
fonte
22
Observe que você pode usar um IEqualityComparer como parâmetro, como .Distinct(StringComparer.OrdinalIgnoreCase)para obter um conjunto distinto de distinção entre maiúsculas e minúsculas.
Just13
Distinct honors é a ordem original dos elementos?
Asyrov
@asyrov: do MSDN:The Distinct() method returns an unordered sequence that contains no duplicate values.
tigrou
52

Aqui está a abordagem 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;
}

Infelizmente, esta solução também requer o .NET framework 3.5 ou posterior, pois o HashSet não foi adicionado até essa versão. Você também pode usar array.Distinct () , que é um recurso do LINQ.

Arcturus
fonte
11
Provavelmente isso não preservará a ordem original.
Hamish Grubijan
11

O seguinte código testado e funcional removerá duplicatas de uma matriz. Você deve incluir o espaço para nome 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]);
}

Você pode agrupar isso em uma função, se quiser.

GateKiller
fonte
Esta parece ser O (n ^ 2) ... Você pode usar uma pilha em vez de um ArrayList
Neil Chowdhury
10

Se você precisar classificá-lo, poderá implementar uma classificação que também remova duplicatas.

Mata dois coelhos com uma cajadada, então.

Matthew Schinckel
fonte
7
Como a classificação remove duplicatas?
Dan1
2
Quem votou nisso? Esta não é uma resposta. "Como faço panquecas?" "Coloque alguns ingredientes em um arco e misture."
Quarkly 04/04
9

Isso pode depender de quanto você deseja criar a solução - se a matriz nunca for tão grande e você não se importar em classificar a lista, poderá tentar algo semelhante ao seguinte:

    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
fonte
4
Você deve usar a lista em vez de ArrayList.
Doug S
7

- Esta é a pergunta da entrevista toda vez. Agora eu fiz sua codificação.

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
fonte
3
Você não deve fazer uma complexidade de tempo O (n * 2) para essa pergunta.
Dan1
2
Você deve usar a classificação por mesclagem
Nick Gallimore
7
List<String> myStringList = new List<string>();
foreach (string s in myStringArray)
{
    if (!myStringList.Contains(s))
    {
        myStringList.Add(s);
    }
}

Este é O (n ^ 2) , que não importa para uma lista curta que será inserida em um combo, mas pode ser rapidamente um problema em uma grande coleção.

Will Dean
fonte
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
fonte
6

Aqui está uma abordagem O (n * n) que usa o espaço 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';
    }
}

As abordagens hash / linq acima são as que você usaria geralmente na vida real. No entanto, nas entrevistas, eles geralmente querem colocar algumas restrições, por exemplo, espaço constante que exclui hash ou nenhuma API interna - que exclui o uso do LINQ .

Sesh
fonte
1
Como ele pode usar o espaço O (1) quando você precisa armazenar a lista inteira? Iniciando com uma classificação local, é possível executar o tempo O (nlogn) e a memória O (n), com muito menos código.
Thomas Ahle
1
O que faz você pensar que está armazenando a lista inteira? Na verdade, está indo no lugar. E, embora não seja uma condição na pergunta, meu código mantém a ordem dos caracteres na string original. A classificação removerá isso.
Sesh
1
O loop interno ( strIn[j] == strIn[i]) comparará uma string consigo mesma, a menos que seja contabilizada com uma instrução if.
User3219
5

Adicione todas as strings a um dicionário e obtenha a propriedade Keys posteriormente. Isso produzirá cada sequência única, mas não necessariamente na mesma ordem em que a entrada original as incluiu.

Se você precisar que o resultado final tenha a mesma ordem que a entrada original, quando considerar a primeira ocorrência de cada sequência, use o seguinte algoritmo:

  1. Tenha uma lista (saída final) e um dicionário (para verificar se há duplicatas)
  2. Para cada sequência na entrada, verifique se ela já existe no dicionário
  3. Caso contrário, adicione-o ao dicionário e à lista

No final, a lista contém a primeira ocorrência de cada sequência exclusiva.

Lembre-se de considerar coisas como cultura e outras coisas ao construir seu dicionário, para lidar com duplicatas com letras acentuadas corretamente.

Lasse V. Karlsen
fonte
5

O seguinte trecho de código tenta remover duplicatas de um ArrayList, embora essa não seja uma solução ideal. Fiz a pergunta durante uma entrevista para remover duplicatas por meio de recursão e sem usar um segundo / temp 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);
            }
        }
Vijay Swami
fonte
5

Solução simples:

using System.Linq;
...

public static int[] Distinct(int[] handles)
{
    return handles.ToList().Distinct().ToArray();
}
Fábio Delboni
fonte
5

Talvez o hashset que não armazena elementos duplicados e ignora silenciosamente os pedidos para adicionar duplicados.

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
fonte
4

NOTA: NÃO testado!

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

Pode fazer o que você precisa ...

EDIT Argh !!! espancado por rob por menos de um minuto!

ZombieSheep
fonte
Rob não venceu você em nada. Ele está usando ArrayList, enquanto você está usando List. Sua versão é melhor.
Doug S
4

Testado abaixo e funciona. O legal é que ele também faz uma pesquisa sensível à 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

AptSenSDET
fonte
4

Esse código 100% remove valores duplicados de uma matriz [como usei um [i]] ..... Você pode convertê-lo em qualquer idioma 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
fonte
4

Método de extensão genérica:

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
fonte
1

você pode usar este código quando trabalhar com um 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
fonte
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
fonte
0

Abaixo está uma lógica simples em java: você percorre os elementos da matriz duas vezes e, se vir algum elemento, atribui zero a ela e não toca no índice do elemento que está comparando.

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
fonte
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
fonte
1
explique sua resposta, por favor.
21417 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
fonte
Esta é uma maneira muito ineficiente de fazer isso. Dê uma olhada nas outras respostas para ver o que elas fazem.
Wai Ha Lee
0
strINvalues = "1,1,2,2,3,3,4,4";
strINvalues = string.Join(",", strINvalues .Split(',').Distinct().ToArray());
Debug.Writeline(strINvalues);

Kkk Não tenho certeza se isso é bruxaria ou apenas código bonito

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

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

1 Dividindo a matriz e usando Distinct [LINQ] para remover duplicatas 2 Juntando-a novamente sem as duplicatas.

Desculpe, eu nunca li o texto no StackOverFlow apenas o código. faz mais sentido do que o texto;)

Kudakwashe Mafutah
fonte
Respostas somente de código são de baixa qualidade. Adicione algumas explicações sobre por que isso funciona.
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
fonte
1
Bem-vindo ao SO. Embora esse snippet de código possa ser a solução, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
alan.elkin
Lamentavelmente, esse código não remove nada e, portanto, não remove duplicatas.
P_P 13/06
0

A melhor maneira? Difícil dizer, a abordagem HashSet parece rápida, mas (dependendo dos dados) usando um algoritmo de classificação (CountSort?) Pode ser muito mais rápido.

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

Quase ramo livre. Quão? Modo de depuração, Step Into (F11) com uma pequena matriz: {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;
    }

Uma solução com dois loops aninhados pode levar algum tempo, especialmente para matrizes maiores.

    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
fonte