Come selezionare elementi unici

Vorrei estendere la class Array con un metodo uniq_elements che restituisce quegli elementi con molteplicità di uno. Vorrei anche usare chiusure per il mio nuovo metodo come con uniq . Per esempio:

 t=[1,2,2,3,4,4,5,6,7,7,8,9,9,9] t.uniq_elements # => [1,3,5,6,8] 

Esempio con chiusura:

 t=[1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] t.uniq_elements{|z| z.round} # => [2.0, 5.1] 

tt.uniqt.to_set-t.uniq.to_set funziona. Non mi interessa la velocità, la chiamo solo una volta nel mio programma, quindi può essere un po ‘lento.

    Metodo di supporto

    Questo metodo utilizza l’helper:

     class Array def difference(other) h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 } reject { |e| h[e] > 0 && h[e] -= 1 } end end 

    Questo metodo è simile a Array # – . La differenza è illustrata nel seguente esempio:

     a = [3,1,2,3,4,3,2,2,4] b = [2,3,4,4,3,4] a - b #=> [1] c = a.difference b #=> [1, 3, 2, 2] 

    Come vedi, a contiene tre 3 e b contiene due, quindi i primi due 3 in a vengono rimossi nella costruzione di c ( a non è mutato). Quando b contiene almeno tante istanze di un elemento come a , c non contiene istanze di quell’elemento. Per rimuovere elementi che iniziano alla fine di a :

     a.reverse.difference(b).reverse #=> [3, 1, 2, 2] 

    Array#difference! potrebbe essere definito in modo ovvio.

    Ho trovato molti usi per questo metodo: qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui , qui e qui .

    Ho proposto di aggiungere questo metodo al core di Ruby.

    Se utilizzato con l’ Array#- , questo metodo semplifica l’estrazione degli elementi univoci da un array a :

     a = [1,3,2,4,3,4] u = a.uniq #=> [1, 2, 3, 4] u - a.difference(u) #=> [1, 2] 

    Funziona perché

     a.difference(u) #=> [3,4] 

    contiene tutti gli elementi non univoci di a (ognuno eventualmente più di una volta).

    Problema a portata di mano

    Codice

     class Array def uniq_elements(&prc) prc ||= ->(e) { e } a = map { |e| prc[e] } u = a.uniq uniques = u - a.difference(u) select { |e| uniques.include?(prc[e]) ? (uniques.delete(e); true) : false } end end 

    Esempi

     t = [1,2,2,3,4,4,5,6,7,7,8,9,9,9] t.uniq_elements #=> [1,3,5,6,8] t = [1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] t.uniq_elements { |z| z.round } # => [2.0, 5.1] 

    Ecco un altro modo.

    Codice

     require 'set' class Array def uniq_elements(&prc) prc ||= ->(e) { e } uniques, dups = {}, Set.new each do |e| k = prc[e] ((uniques.key?(k)) ? (dups < < k; uniques.delete(k)) : uniques[k] = e) unless dups.include?(k) end uniques.values end end 

    Esempi

     t = [1,2,2,3,4,4,5,6,7,7,8,9,9,9] t.uniq_elements #=> [1,3,5,6,8] t = [1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] t.uniq_elements { |z| z.round } # => [2.0, 5.1] 

    Spiegazione

    • se uniq_elements viene chiamato con un blocco, viene ricevuto come proc prc .
    • se uniq_elements viene chiamato senza un blocco, prc è nil , quindi la prima istruzione del metodo imposta prc uguale al proc predefinito (lambda).
    • un hash inizialmente vuoto, uniques , contiene rappresentazioni dei valori univoci. I valori sono i valori univoci dell'array self , le chiavi sono ciò che viene restituito quando il prc proc è passato al valore dell'array e chiamato: k = prc[e] .
    • il set dups contiene gli elementi dell'array che hanno trovato non essere unici. È un set (piuttosto che un array) per velocizzare le ricerche. In alternativa, se potrebbe essere un hash con i valori non univoci come chiavi e valori arbitrari.
    • i seguenti passaggi vengono eseguiti per ciascun elemento e dell'array self :
      • k = prc[e] è calcolato.
      • se dups contiene k , e è un dup, quindi non c'è bisogno di fare altro; altro
      • se uniques ha una chiave k , e è un dup, quindi k viene aggiunto al set dups e l'elemento con la chiave k viene rimosso da uniques ; altro
      • l'elemento k=>e viene aggiunto agli uniques come candidato per un elemento unico.
    • i valori di unique vengono restituiti.
     class Array def uniq_elements counts = Hash.new(0) arr = map do |orig_val| converted_val = block_given? ? (yield orig_val) : orig_val counts[converted_val] += 1 [converted_val, orig_val] end uniques = [] arr.each do |(converted_val, orig_val)| uniques < < orig_val if counts[converted_val] == 1 end uniques end end t=[1,2,2,3,4,4,5,6,7,7,8,9,9,9] p t.uniq_elements t=[1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] p t.uniq_elements { |elmt| elmt.round } --output:-- [1, 3, 5, 6, 8] [2.0, 5.1] 

    Array # uniq non trova elementi non duplicati, piuttosto l'array # uniq rimuove i duplicati.

     class Array def uniq_elements zip( block_given? ? map { |e| yield e } : self ) .each_with_object Hash.new do |(e, v), h| h[v] = h[v].nil? ? [e] : false end .values.reject( &:! ).map &:first end end [1,2,2,3,4,4,5,6,7,7,8,9,9,9].uniq_elements #=> [1, 3, 5, 6, 8] [1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2].uniq_elements &:round #=> [2.0, 5.1] 
    1. Creare e chiamare un proc predefinito è una perdita di tempo, e
    2. Cramming tutto in una sola riga usando costrutti torturati non rende il codice più efficiente, ma rende il codice più difficile da capire.
    3. Nelle dichiarazioni require, i rubyists non inseriscono in maiuscolo i nomi dei file.

    ….

     require 'set' class Array def uniq_elements uniques = {} dups = Set.new each do |orig_val| converted_val = block_given? ? (yield orig_val) : orig_val next if dups.include? converted_val if uniques.include?(converted_val) uniques.delete(converted_val) dups < < converted_val else uniques[converted_val] = orig_val end end uniques.values end end t=[1,2,2,3,4,4,5,6,7,7,8,9,9,9] p t.uniq_elements t=[1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] p t.uniq_elements {|elmt| elmt.round } --output:-- [1, 3, 5, 6, 8] [2.0, 5.1]