Bir dizinin başka bir dizinin tüm öğelerini içerip içermediğini belirleme


179

Verilen:

a1 = [5, 1, 6, 14, 2, 8]

Tüm unsurları içerip içermediğini belirlemek istiyorum:

a2 = [2, 6, 15]

Bu durumda sonuç false.

Bu tür bir dizi dahil etmeyi tanımlamak için yerleşik Ruby / Rails yöntemleri var mı?

Bunu uygulamanın bir yolu:

a2.index{ |x| !a1.include?(x) }.nil?

Daha iyi, daha okunabilir bir yol var mı?


Kabul edilen cevap (dizi çıkarma) en hızlı çözümdür. Hepsini burada karşılaştırdım: gist.github.com/bbugh/cbbde8b48cbb16286044f6893e1f2e5f
brainbag

Yanıtlar:


309
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

a - b
=> [5, 1, 14, 8]

b - a
=> [15]

(b - a).empty?
=> false

61
Bu yol. Sadece biraz kısaltılmış olabilir(a2-a1).empty?
Holger Just

9
Bu sadece set dizileri için geçerlidir, kopyaları olan diziler için değil
Chris

3
@Chris - Bunun için Array # uniq'i kullanmayı deneyebilirsiniz. Holger Just'ın örneği ile bu olurdu(a2.uniq - a1.uniq).empty?
Nick

stackoverflow.com/questions/13553822/… demek istediğim bu. Unique # dizisi açıkça başarısız olur.
Chris

81

Belki de bunu okumak daha kolaydır:

a2.all? { |e| a1.include?(e) }

Dizi kesişimini de kullanabilirsiniz:

(a1 & a2).size == a1.size

sizeBurada sadece hız için kullanıldığını da unutmayın (daha yavaş):

(a1 & a2) == a1

Ama sanırım birincisi daha okunabilir. Bu üçü düz yakuttur (ray değil).


OP'nin a1 ve a2 ve "a2'nin tüm öğelerini içeren a1" tanımını kullanıyorsanız, bunun _ (a1 & a2) olması gerektiğini düşünüyorum .size == a2.size _ çünkü a2, daha büyük diziye dahil edilen elemanlar ('true' elde etmek için) - bu nedenle, içindeki tüm elemanlar daha büyük olan mevcutsa, iki dizinin kesişimi daha küçük dizi ile aynı olmalıdır.
JosephK

57

Bu,

(a2 & a1) == a2

Bu gelen tüm unsurları dönen, her iki dizilerin kesişimini yaratır a2da vardır ki a1. Sonuç ile aynıysa a2, tüm öğelerin içinde bulunduğundan emin olabilirsiniz.a1 .

Bu yaklaşım, ancak başlangıçta tüm unsurlar a2birbirinden farklıysa işe yarar . Çiftler varsa, bu yaklaşım başarısız olur. O zaman Tempos'tan biri hala çalışıyor, bu yüzden gönülden yaklaşımını öneriyorum (muhtemelen daha hızlı).


2
lengthyöntemi kullanarak çok daha iyi performans
Pablo Fernandez

3
Kesişim kümesinin öğeleri aynı sırayla varsa bu çalışmaz. Bu soruyu cevaplamaya çalışırken bunu zor yoldan öğrendim: stackoverflow.com/questions/12062970/… daha sonra birçok akıllı insanın burada zaten yapmış olduğunu fark etti!
CubaLibre

1
@CubaLibre İlginç. Bunu yeniden üretmek için bazı test verileriniz var mı? Testlerimden sonuçta ortaya çıkan dizi, ilk dizideki öğelerin sırasını koruyor gibi görünüyordu (dolayısıyla cevabımdaki en son düzenlemem). Ancak, durum gerçekten böyle değilse, öğrenmek isterim.
Holger Sadece

@HolgerJust (a2 & a1) yerine (a1 & a2) yapma hatasını yapmıştım, bu yüzden hatayı görüyordum. İlk diziden siparişi koruma ve hakkını saklı tutuyorsunuz.
CubaLibre

10

Yinelenen öğe yoksa veya bu öğelerle ilgilenmiyorsanız Set sınıfını kullanabilirsiniz :

a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false

Kullandığı perde arkasında

all? { |o| set.include?(o) }

1

Array sınıfını maymun yaması yapabilirsiniz:

class Array
    def contains_all?(ary)
        ary.uniq.all? { |x| count(x) >= ary.count(x) }
    end
end

Ölçek

irb(main):131:0> %w[a b c c].contains_all? %w[a b c]
=> true
irb(main):132:0> %w[a b c c].contains_all? %w[a b c c]
=> true
irb(main):133:0> %w[a b c c].contains_all? %w[a b c c c]
=> false
irb(main):134:0> %w[a b c c].contains_all? %w[a]
=> true
irb(main):135:0> %w[a b c c].contains_all? %w[x]
=> false
irb(main):136:0> %w[a b c c].contains_all? %w[]
=> true
irb(main):137:0> %w[a b c d].contains_all? %w[d c h]
=> false
irb(main):138:0> %w[a b c d].contains_all? %w[d b c]
=> true

Elbette yöntem standart bir yöntem olarak yazılabilir, örn.

def contains_all?(a,b)
    b.uniq.all? { |x| a.count(x) >= b.count(x) }
end

ve onu şöyle çağırabilirsiniz

contains_all?(%w[a b c c], %w[c c c])

Aslında, profil oluşturduktan sonra, aşağıdaki sürüm çok daha hızlıdır ve kod daha kısadır.

def contains_all?(a,b)
    b.all? { |x| a.count(x) >= b.count(x) }
end

0

Dizilerinizin ne kadar büyük olduğuna bağlı olarak verimli bir algoritma O (n log n) düşünebilirsiniz.

def equal_a(a1, a2)
  a1sorted = a1.sort
  a2sorted = a2.sort
  return false if a1.length != a2.length
  0.upto(a1.length - 1) do 
    |i| return false if a1sorted[i] != a2sorted[i]
  end
end

Sıralama maliyetleri O (n log n) ve her bir çifti kontrol etmek O (n) maliyeti dolayısıyla algoritma O (n log n) olur. Diğer algoritmalar sıralanmamış diziler kullanılarak daha hızlı (asimptotik) olamaz.


O (n) 'de sayım sıralamasıyla yapabilirsiniz.
klochner

Hayır yapamazsın. Sayma sıralaması sınırlı bir evren kullanır ve Ruby büyük sayıların nasıl elde edilebileceği konusunda bir sınırlamaya sahip değildir.
ayckoster

Öğeleri sıralamak zorunda olmadığınız için - her iki dizi için de bir karma eşleme öğesine -> saymaya, ardından anahtarların üzerinde tekrarlamaya ve sayıları karşılaştırmaya ihtiyacınız vardır.
klochner

Array # sort'in birleştirme sıralaması kullandığından emin misiniz?
Nate Symer

0

Her iki dizide de yinelenen öğeler varsa (a1 - a2) veya (a1 & a2) temelli yanıtların çoğu çalışmaz. Buraya bir kelimenin tüm harflerinin (bir diziye bölünmüş) harf kümesinin (örneğin scrabble için) bir parçası olup olmadığını görmek için bir yol aramaya geldi. Bu cevapların hiçbiri işe yaramadı, ama bu cevap verdi:

def contains_all?(a1, a2)
  try = a1.chars.all? do |letter|
    a1.count(letter) <= a2.count(letter)
  end
  return try
end
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.