arr
é uma matriz de strings:
["hello", "world", "stack", "overflow", "hello", "again"]
Qual seria uma maneira fácil e elegante de verificar se arr
há duplicatas e, em caso afirmativo, retornar uma delas (não importa qual)?
Exemplos:
["A", "B", "C", "B", "A"] # => "A" or "B"
["A", "B", "C"] # => nil
arr == arr.uniq
seria uma maneira fácil e elegante de verificar searr
há duplicatas, no entanto, não fornece quais foram duplicadas.Respostas:
Sei que não é uma resposta muito elegante, mas adoro. É um código liner bonito. E funciona perfeitamente bem, a menos que você precise processar um enorme conjunto de dados.
Procurando uma solução mais rápida? Aqui está!
É linear, O (n), mas agora precisa gerenciar várias linhas de código, precisa de casos de teste etc.
Se você precisar de uma solução ainda mais rápida, talvez tente C.
E aqui está a essência comparando diferentes soluções: https://gist.github.com/naveed-ahmad/8f0b926ffccf5fbd206a1cc58ce9743e
fonte
a.select {|e| a.count(e) > 1}.uniq
Você pode fazer isso de várias maneiras, sendo a primeira opção a mais rápida:
E uma opção O (N ^ 2) (ou seja, menos eficiente):
fonte
group_by.select
ary.group_by(&:itself)
. :-)Basta encontrar a primeira instância em que o índice do objeto (contando da esquerda) não é igual ao índice do objeto (contando da direita).
Se não houver duplicatas, o valor de retorno será nulo.
Eu acredito que esta é a solução mais rápida postada no thread até agora também, já que não depende da criação de objetos adicionais
#index
e#rindex
é implementada em C. O tempo de execução do big-O é N ^ 2 e, portanto, mais lento que Sergio, mas o tempo de parede pode ser muito mais rápido devido ao fato de as partes "lentas" rodarem em C.fonte
arr.find_all {|e| arr.rindex(e) != arr.index(e) }.uniq
arr.detect.with_index { |e, idx| idx != arr.rindex(e) }
. O usowith_index
deve remover a necessidade da primeiraindex
pesquisa.detect
encontra apenas uma duplicata.find_all
encontrará todos eles:fonte
count
para todos os elementos da matriz. (Um hash de contagem, por exemplo, é muito mais eficiente, por exemplo, construah = {"A"=>2, "B"=>2, "C"=> 1 }
em seguidah.select { |k,v| v > 1 }.keys #=> ["A", "B"]
.Aqui estão mais duas maneiras de encontrar uma duplicata.
Use um conjunto
Use
select
no lugar defind
para retornar uma matriz de todas as duplicatas.Usar
Array#difference
Solte
.first
para retornar uma matriz de todas as duplicatas.Ambos os métodos retornam
nil
se não houver duplicatas.Eu propus que
Array#difference
fosse adicionado ao núcleo do Ruby. Mais informações estão na minha resposta aqui .Referência
Vamos comparar os métodos sugeridos. Primeiro, precisamos de uma matriz para testar:
e um método para executar os benchmarks para diferentes matrizes de teste:
Não incluí a resposta de @ JjP porque apenas uma duplicata deve ser retornada e, quando sua resposta é modificada para fazer isso, é igual à resposta anterior de @ Naveed. Também não incluí a resposta de @ Marin, que, embora postada antes da resposta de @ Naveed, retornava todas as duplicatas em vez de apenas uma (um ponto menor, mas não faz sentido avaliar as duas, pois são idênticas quando retornam apenas uma duplicata).
Também modifiquei outras respostas que retornaram todas as duplicatas para retornar apenas a primeira encontrada, mas que não deveriam ter nenhum efeito sobre o desempenho, pois calculavam todas as duplicatas antes de selecionar uma.
Os resultados de cada benchmark estão listados do mais rápido ao mais lento:
Primeiro, suponha que a matriz contenha 100 elementos:
Agora considere uma matriz com 10.000 elementos:
Observe que
find_a_dup_using_difference(arr)
seria muito mais eficiente seArray#difference
fosse implementado em C, o que seria o caso se fosse adicionado ao núcleo do Ruby.Conclusão
Muitas das respostas são razoáveis, mas usar um Conjunto é a melhor opção . É o mais rápido nos casos médios, o mais rápido nos casos mais difíceis e apenas nos casos computacionalmente triviais - quando sua escolha não importa de qualquer maneira - pode ser derrotado.
O único caso muito especial em que você pode escolher a solução de Chris seria se você deseja usar o método para desduplicar separadamente milhares de arrays pequenos e esperar encontrar um duplicado normalmente com menos de 10 itens. Isso será um pouco mais rápido pois evita a pequena sobrecarga adicional de criar o conjunto.
fonte
Infelizmente, a maioria das respostas são
O(n^2)
.Aqui está uma
O(n)
solução,Qual é a complexidade disso?
O(n)
e quebra na primeira partidaO(n)
memória, mas apenas a quantidade mínimaAgora, dependendo da frequência de duplicatas em sua matriz, esses tempos de execução podem se tornar ainda melhores. Por exemplo, se a matriz de tamanho
O(n)
tiver sido amostrada de uma população dek << n
elementos diferentes, apenas a complexidade do tempo de execução e do espaço se tornaráO(k)
; no entanto, é mais provável que o pôster original esteja validando a entrada e queira garantir que não haja duplicatas. Nesse caso, o tempo de execução e a complexidade da memória,O(n)
pois esperamos que os elementos não tenham repetições para a maioria das entradas.fonte
Os objetos Ruby Array têm um ótimo método
select
,.A primeira forma é o que lhe interessa aqui. Permite selecionar objetos que passam no teste.
Os objetos Ruby Array possuem outro método
count
,.Nesse caso, você está interessado em duplicatas (objetos que aparecem mais de uma vez na matriz). O teste apropriado é
a.count(obj) > 1
.Se
a = ["A", "B", "C", "B", "A"]
entãoVocê afirma que deseja apenas um objeto. Então escolha um.
fonte
["A", "B", "B", "A"]
.uniq
na matriz.count
para cada elemento da matriz, o que é um desperdício e desnecessário. Veja meu comentário na resposta de JjP.find_all () retorna um
array
contendo todos os elementos dosenum
quaisblock
não éfalse
.Para obter
duplicate
elementosOu duplicar
uniq
elementosfonte
Algo assim vai funcionar
Ou seja, coloque todos os valores em um hash, onde key é o elemento da matriz e value é o número de ocorrências. Em seguida, selecione todos os elementos que ocorrem mais de uma vez. Fácil.
fonte
Eu sei que esse tópico é sobre Ruby especificamente, mas cheguei aqui procurando como fazer isso dentro do contexto do Ruby on Rails com o ActiveRecord e pensei em compartilhar minha solução também.
O exemplo acima retorna uma matriz de todos os endereços de email duplicados na tabela de banco de dados deste exemplo (que no Rails seria "active_record_classes").
fonte
Este é um
O(n)
procedimento.Como alternativa, você pode executar uma das seguintes linhas. Também O (n), mas apenas uma iteração
fonte
Aqui está minha opinião sobre um grande conjunto de dados - como uma tabela legada do dBase para encontrar partes duplicadas
fonte
fonte
each_with_object
é seu amigo!fonte
Este código retornará uma lista de valores duplicados. As chaves de hash são usadas como uma maneira eficiente de verificar quais valores já foram vistos. Com base em se o valor foi visto, a matriz original
ary
é particionada em 2 matrizes: primeiro contendo valores exclusivos e segundo contendo duplicatas.Você pode reduzi-lo ainda mais - embora a um custo de sintaxe um pouco mais complexa - para este formato:
fonte
Resultados
fonte
Se você estiver comparando duas matrizes diferentes (em vez de uma contra ela mesma), uma maneira muito rápida é usar o operador de interseção
&
fornecido pela classe Ruby Array .fonte
Eu precisava descobrir quantas duplicatas havia e o que eram, então escrevi uma função baseada no que Naveed havia publicado anteriormente:
fonte
vamos demonstrar na implementação de código
Agora chame o método de duplicação e o resultado do retorno de saída -
fonte
[1,2,3].uniq!.nil? => true
[1,2,3,3].uniq!.nil? => false
Observe que o acima é destrutivo
fonte