it-swarm.it

Come determinare se un array contiene tutti gli elementi di un altro array

Dato:

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

Vorrei determinare se contiene tutti gli elementi di:

a2 = [2, 6, 15]

In questo caso il risultato è false.

Esistono metodi Ruby/Rails integrati per identificare tale inclusione di array?

Un modo per implementare questo è:

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

Esiste un modo migliore, più leggibile?

165
Misha Moroshko
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

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

b - a
=> [15]

(b - a).empty?
=> false
289
Geo

Forse è più facile da leggere:

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

È inoltre possibile utilizzare l'intersezione di array:

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

Nota che size è usato qui solo per la velocità, puoi anche fare (più lentamente):

(a1 & a2) == a1

Ma immagino che il primo sia più leggibile. Questi 3 sono semplici Ruby (non Rails).

71
Pablo Fernandez

Questo può essere ottenuto facendo

(a2 & a1) == a2

Questo crea l'intersezione di entrambi gli array, restituendo tutti gli elementi da a2 che sono anche in a1. Se il risultato è uguale a a2, puoi essere sicuro di avere tutti gli elementi inclusi in a1.

Questo approccio funziona solo se tutti gli elementi in a2 sono diversi l'uno dall'altro in primo luogo. Se ci sono doppi, questo approccio fallisce. Quello di Tempos funziona ancora allora, quindi consiglio vivamente il suo approccio (anche probabilmente è più veloce).

53
Holger Just

Se non ci sono elementi duplicati o non ti interessa, puoi usare la classe Set :

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

Dietro le quinte questo usa

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

Puoi applicare la patch di scimmia alla classe Array:

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

test

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

Naturalmente il metodo può essere scritto come un metodo standard solo, ad es

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

e puoi invocarlo come

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

In effetti, dopo la profilazione, la seguente versione è molto più veloce e il codice è più breve.

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

La maggior parte delle risposte basate su (a1 - a2) o (a1 e a2) non funzionerebbe se ci fossero elementi duplicati in uno degli array. Sono arrivato qui alla ricerca di un modo per vedere se tutte le lettere di una parola (divise in una matrice) facevano parte di un insieme di lettere (per esempio scrabble). Nessuna di queste risposte ha funzionato, ma questa fa:

def contains_all?(a1, a2)
  try = a1.chars.all? do |letter|
    a1.count(letter) <= a2.count(letter)
  end
  return try
end
0
Charles Breton

A seconda di quanto sono grandi i tuoi array potresti considerare un algoritmo efficiente O (n log n)

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

L'ordinamento dei costi O (n log n) e il controllo di ogni coppia costa O(n) quindi questo algoritmo è O (n log n). Gli altri algoritmi non possono essere più veloci (asintoticamente) utilizzando array non ordinati.

0
ayckoster