Entfernen Sie doppelte Elemente aus dem Array in Ruby

325

Ich habe ein Ruby-Array, das doppelte Elemente enthält.

array = [1,2,2,1,4,4,5,6,7,8,5,6]

Wie kann ich alle doppelten Elemente aus diesem Array entfernen und dabei alle eindeutigen Elemente beibehalten, ohne for-Schleifen und Iteration zu verwenden?

Mithun Sasidharan
quelle

Antworten:

721
array = array.uniq

uniq Entfernt alle doppelten Elemente und behält alle eindeutigen Elemente im Array bei.

Dies ist eine von vielen Schönheiten der Ruby-Sprache.

Mithun Sasidharan
quelle
50
nein, die uniq! Die Methode gibt nil zurück, wenn das Array noch eindeutig war. Beispiel: a = [1,2,3,4] a.uniq -> [1,2,3,4] aber a.uniq! -> nil
duykhoa
15
Ich würde das nicht wirklich als Schönheit der Ruby-Sprache sehen ... es ist nur die Schönheit der Ruby-Standardbibliothek? Versteh mich nicht falsch, es gibt viele schöne Dinge an der Sprache.
Justin L.
7
Schreiben Sie dasselbe in Objective-C, Javascript und PHP. Dann sag uns, dass Ruby keine schöne Sprache ist!
Adam Waite
3
Dies funktioniert auch für komplexe Typen: [{how: "are"}, {u:"doing"}, {how: "are"}].uniq => [{:how=>"are"}, {:u=>"doing"}]
Blaskovicz
5
über das, was @duykhoa sagt, die Uniq! Methode gibt null zurück, aber Sie kümmern sich normalerweise nicht um die Rückgabe von a .uniq!erledigt die Arbeit am Objekt selbst
carpinchosaurio
82

Sie können die Kreuzung zurückgeben.

a = [1,1,2,3]
a & a

Dadurch werden auch Duplikate gelöscht.

Jaredsmith
quelle
12
Funktionell ist diese Antwort richtig, aber ich denke, dies ist deutlich weniger lesbar als nur die Verwendung von uniq.
Fiona T
21
Ich habe es nur hier platziert, damit jeder, der diese Seite besucht, auch andere Möglichkeiten sieht, es zu tun. Ich habe nicht versucht zu sagen, dass es in irgendeiner Weise besser ist.
Jarmsmith
3
Der Grund dafür ist, dass bei Verwendung von Set-Operationen das resultierende Array als Set behandelt wird. Hierbei handelt es sich um eine Datenstruktur, die normalerweise keine Wiederholungswerte aufweist. Die Verwendung von a | a(union) würde den gleichen Trick machen.
Cezar
47

Sie können die doppelten Elemente mit der uniq-Methode entfernen:

array.uniq  # => [1, 2, 4, 5, 6, 7, 8]

Es kann auch nützlich sein zu wissen, dass uniqein Block benötigt wird. Wenn Sie also ein Array von Schlüsseln haben:

["bucket1:file1", "bucket2:file1", "bucket3:file2", "bucket4:file2"]

und wenn Sie wissen möchten, was die eindeutigen Dateien sind, können Sie dies herausfinden mit:

a.uniq { |f| f[/\d+$/] }.map { |p| p.split(':').last }
Marek Příhoda
quelle
5
Das verwirrt mich ein bisschen. Der Block wird verwendet, wenn Sie eine eigene Vergleichsfunktion benötigen. In Ihrem Beispiel würde das Senden uniqan dieses Array ohne Block denselben Wert zurückgeben wie bei Ihrem Block.
HDGARROOD
18

Nur eine andere Alternative, wenn es jemanden interessiert.

Sie können auch die to_setMethode eines Arrays verwenden, das das Array in eine Menge konvertiert. Per Definition sind Mengenelemente eindeutig.

[1,2,3,4,5,5,5,6].to_set => [1,2,3,4,5,6]
Finks
quelle
4
Wenn Sie sich für Speicher interessieren, to_setwerden 4 Objekte uniqzugewiesen , während eines zugewiesen wird.
Jan Klimo
18

Wenn jemand nach einer Möglichkeit gesucht hat, alle Instanzen wiederholter Werte zu entfernen, lesen Sie " Wie kann ich wiederholte Elemente in einem Ruby-Array effizient extrahieren? ".

a = [1, 2, 2, 3]
counts = Hash.new(0)
a.each { |v| counts[v] += 1 }
p counts.select { |v, count| count == 1 }.keys # [1, 3]
Lri
quelle
3
Oder könnte es einfach tun a = [1, 2, 2, 3] a.find_all { |x| a.count(x) == 1 } # [1, 3]
Tim Wright
Die verknüpfte Frage ist nicht dieselbe. Es wird gefragt, wie doppelte Werte gefunden und zurückgegeben werden können. Das OP möchte Duplikate entfernen.
Der Blechmann
0

Nur um einen Einblick zu geben:

require 'fruity'
require 'set'

array = [1,2,2,1,4,4,5,6,7,8,5,6] * 1_000

def mithun_sasidharan(ary)
  ary.uniq
end

def jaredsmith(ary)
  ary & ary
end

def lri(ary)
  counts = Hash.new(0)
  ary.each { |v| counts[v] += 1 }
  counts.select { |v, count| count == 1 }.keys 
end

def finks(ary)
  ary.to_set
end

def santosh_mohanty(ary)
    result = ary.reject.with_index do |ele,index|
      res = (ary[index+1] ^ ele)
      res == 0
    end
end

SHORT_ARRAY = [1,1,2,2,3,1]
mithun_sasidharan(SHORT_ARRAY) # => [1, 2, 3]
jaredsmith(SHORT_ARRAY) # => [1, 2, 3]
lri(SHORT_ARRAY) # => [3]
finks(SHORT_ARRAY) # => #<Set: {1, 2, 3}>
santosh_mohanty(SHORT_ARRAY) # => [1, 2, 3, 1]

puts 'Ruby v%s' % RUBY_VERSION

compare do
  _mithun_sasidharan { mithun_sasidharan(array) }
  _jaredsmith { jaredsmith(array) }
  _lri { lri(array) }
  _finks { finks(array) }
  _santosh_mohanty { santosh_mohanty(array) }
end

Was beim Ausführen zu Folgendem führt:

# >> Ruby v2.7.1
# >> Running each test 16 times. Test will take about 2 seconds.
# >> _mithun_sasidharan is faster than _jaredsmith by 2x ± 0.1
# >> _jaredsmith is faster than _santosh_mohanty by 4x ± 0.1 (results differ: [1, 2, 4, 5, 6, 7, 8] vs [1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, ...
# >> _santosh_mohanty is similar to _lri (results differ: [1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, ...
# >> _lri is similar to _finks (results differ: [] vs #<Set: {1, 2, 4, 5, 6, 7, 8}>)

Hinweis: Diese lieferten schlechte Ergebnisse:

  • lri(SHORT_ARRAY) # => [3]
  • finks(SHORT_ARRAY) # => #<Set: {1, 2, 3}>
  • santosh_mohanty(SHORT_ARRAY) # => [1, 2, 3, 1]
der Blechmann
quelle
-4

Versuchen Sie es mit dem XOR-Operator, ohne die integrierten Funktionen zu verwenden:

a = [3,2,3,2,3,5,6,7].sort!

result = a.reject.with_index do |ele,index|
  res = (a[index+1] ^ ele)
  res == 0
end

print result

Mit eingebauten Funktionen:

a = [3,2,3,2,3,5,6,7]

a.uniq
Santosh Mohanty
quelle
2
Ich habe nicht herabgestimmt und weiß so gut wie nichts über Ruby, ist aber nicht .sort!auch eine eingebaute Funktion?
Carolus