2015-11-06 8 views
6

Ho una matrice di hash che ho bisogno di ordinare in base a due coppie di valori chiave differenti.Array di ordinamento personalizzato di hash basato su più coppie chiave/valore

Qui è l'array sto cercando di risolvere:

array_group = [ 
{operator: OR, name: "some string", status: false}, 
{operator: AND, name: "other string", status: false}, 
{operator: _NOT_PRESENT, name: "another string", status: true}, 
{operator: AND, name: "just string", status: true} 
] 

voglio ordinare array_group quindi non ho elementi con status: true primo, seguito da status: false, seguito dagli articoli con operator: _NOT_PRESENT e infine sorta si basa sulla nome, con conseguente qualcosa come:

array_group = [ 
{operator: AND, name: "just string", status: true}, 
{operator: AND, name: "other string", status: false}, 
{operator: OR, name: "some string", status: false}, 
{operator: _NOT_PRESENT, name: "another string", status: true}, 
] 

c'è un modo che io possa ottenere questo fatto senza creare sotto-array e smistarli e concatenando indietro?

+0

La dichiarazione della sua domanda suggerisce che 'status' può avere un valore diverso da' True' o 'False'. È corretto? –

+0

sì lo stato non è in realtà un booleano è una stringa che in seguito si trasformerà in valori booleani se è vero o falso in qualche altra parte del codice – sujay

+1

In che modo "l'altra stringa" '' s 'status' passa da' true' a 'falso'? Perché '' un'altra stringa ''non è la prima nell'array ordinato, si consideri che si lega con' "other stringa" 'sul valore di': status' ed è l'unico dei due ad avere '_NOT_PRESENT' come valore di ': operatore'? –

risposta

5

È inoltre possibile utilizzare Enumerable#sort_by. L'esempio costruisce un array che viene confrontato elemento per elemento durante l'ordinamento.

array_group.sort_by { |e| [e[:operator] == "_NOT_PRESENT" ? 1 : 0, 
          e[:status] ? 0 : 1, 
          e[:name]] } 

L'esempio sopra ordini record con operator: "_NOT_PRESENT" anche da :status. Il seguente frammento esegue esattamente l'ordine dalla domanda.

def priority(h) 
    case 
    when h[:operator] == "_NOT_PRESENT" then 3 
    when h[:status] == false   then 2 
    # h[:status] == true 
    else 1 
    end 
end 

array_group.sort_by { |e| [priority(e), e[:name]] } 
+1

Se incorporato in un metodo questo non funzionerà perché '_NOT_PRESENT' è una variabile locale. Se cambi '_NOT_PRESENT' a' NOT_PRESENT', sia qui che in 'array_group', dopo aver definito la costante' NOT_PRESENT', ottieni '{: operator =>" e ",: name =>" just string ",: status => true} 'come primo elemento dell'array ordinato, che non è corretto. –

+0

@CarySwoveland Il primo elemento che menzioni è il primo elemento del risultato desiderato. Che cosa c'è che non va? – sschmeck

+0

La prima riga mette "un'altra stringa" dopo tutti gli altri hash (indipendentemente dal valore di ': status', che è il primo filtro).Supponiamo ': status = true' per" un'altra stringa "(come è ora), ma': status = false' per tutti gli altri hash. Quindi "un'altra stringa" dovrebbe essere ordinata per prima, non per ultima. –

1

È possibile utilizzare il metodo Array.sort. Accetta un blocco con due argomenti (x, y), quando x è maggiore di y dovrebbe restituire 1, altrimenti -1 e 0 se sono uguali.

Il codice:

OR = "OR" 
AND = "AND" 
_NOT_PRESENT = "_NOT_PRESENT" 

array_group = [ 
{operator: OR, name: "some string", status: false}, 
{operator: AND, name: "other string", status: true}, 
{operator: _NOT_PRESENT, name: "another string", status: true}, 
{operator: AND, name: "just string", status: true} 
] 

results = array_group.sort do |x, y| 
    next x[:operator] == _NOT_PRESENT ? 1 : -1 if x[:operator] == _NOT_PRESENT || y[:operator] == _NOT_PRESENT 
    next x[:status] ? -1 : 1 if x[:status] != y[:status] 
    next x[:name] <=> y[:name] 
end 

E btw, voi i vostri dati di ingresso e di uscita non corrisponde a vicenda - l'hash con OR è false nell'input, ma true nell'output.

credo che l'output dovrebbe essere simile:

[{:operator=>"AND", :name=>"just string", :status=>true}, 
{:operator=>"AND", :name=>"other string", :status=>true}, 
{:operator=>"OR", :name=>"some string", :status=>false}, 
{:operator=>"_NOT_PRESENT", :name=>"another string", :status=>true}] 

che la produzione sarà effettivamente corrispondere la logica di ordinamento.

+0

grazie per la risposta ,,, scusa per aver fornito informazioni errate ,,, è stato un errore di battitura riguardo una voce ma il mio output finale dovrebbe sempre avere _NOT_PRESENT all'ultimo .. – sujay

+0

Ho aggiunto un'altra versione, quindi _NOT_PRESENTs sono sempre ultimi, altrimenti lo stato == true va prima dello stato == false, quando gli stati sono uguali i nomi vengono confrontati alfabeticamente . –

0

I assumono le costanti specificati OR, AND e NOT_PRESENT sono definite come segue (per esempio): (.. Costanti devono iniziare con una lettera maiuscola _NOT_PRESENT è una variabile locale)

OR = "or" 
AND = "and" 
NOT_PRESENT = "not present" 

Quando si utilizza Enumerable#sort_by, è necessario ordinare su una matrice contenente tre elementi, corrispondenti a :status, :operator e :name, in questo ordine. Il primo di questi tre elementi è più piccola (diciamo 0) se il valore di :status è true, immediatamente inferiore (diciamo 1) se il valore di :status è false e più grande (diciamo 2) se si tratta di qualcos'altro. Abbiamo:

def rearrange(arr) 
    arr.sort_by do |h| 
    [ 
     h[:status]==true ? 0 : (h[:status]==false ? 1 : 2), 
     (h[:operator]==NOT_PRESENT) ? 0 : 1, 
     h[:name] 
    ] 
    end 
end 

array_group = [ 
{operator: AND, name: "just string", status: true}, 
{operator: OR, name: "some string", status: false}, 
{operator: AND, name: "other string", status: false}, 
{operator: NOT_PRESENT, name: "another string", status: true}, 
] 

rearrange(array_group) 
    #=> [{:operator=>"not present", :name=>"another string", :status=>true}, 
    # {:operator=>"and", :name=>"just string", :status=>true}, 
    # {:operator=>"and", :name=>"other string", :status=>false}, 
    # {:operator=>"or", :name=>"some string", :status=>false}] 

Questo non è il risultato desiderato, ma è coerente con la mia comprensione della domanda.

0

È possibile utilizzare Enumerable#sort_by utilizzando un array per mantenere i gruppi desiderati e l'ordinamento per nome senza perdere quei gruppi. Per esempio:

[[0,0,1], [1,1,1], [0,1,0], [1,0,0], [0,1,1]].sort_by &:itself 
# => [[0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 1, 1]] 

Con questo, è possibile impostare un array con lunghezza 2. Utilizzando l'indice 0 al "gruppo" dagli elementi che si desidera e indice 1 per ordinare per nome.

OR = "OR" 
AND = "AND" 
_NOT_PRESENT = "_NOT_PRESENT" 

array_group = [ 
{ operator: OR, name: "z string", status: false }, 
{ operator: AND, name: "a string", status: false }, 
{ operator: AND, name: "z string", status: true }, 
{ operator: OR, name: "a string", status: true }, 
{ operator: _NOT_PRESENT, name: "d string", status: true }, 
{ operator: _NOT_PRESENT, name: "b string", status: true }, 
{ operator: _NOT_PRESENT, name: "c string", status: false }, 
{ operator: _NOT_PRESENT, name: "a string", status: false } 
] 

# Types of "groups" you want to keep. Greater values will be at the end 
BY_NOT_PRESENT = 2 
BY_STATUS_FALSE = 1 
BY_STATUS_TRUE = 0 

array_group.sort_by do |a| 
    group = if a[:operator] == _NOT_PRESENT 
      BY_NOT_PRESENT 
      else 
      a[:status] ? BY_STATUS_TRUE : BY_STATUS_FALSE 
      end 
    [group, a[:name]] 
end 
#=> [{:operator=>"OR", :name=>"a string", :status=>true}, 
#  {:operator=>"AND", :name=>"z string", :status=>true}, 
#  {:operator=>"AND", :name=>"a string", :status=>false}, 
#  {:operator=>"OR", :name=>"z string", :status=>false}, 
#  {:operator=>"_NOT_PRESENT", :name=>"a string", :status=>false}, 
#  {:operator=>"_NOT_PRESENT", :name=>"b string", :status=>true}, 
#  {:operator=>"_NOT_PRESENT", :name=>"c string", :status=>false}, 
#  {:operator=>"_NOT_PRESENT", :name=>"d string", :status=>true}] 

che sto facendo in questo modo, perché se solo sort_by una matrice come [status, not_present, name] poi status avrà la precedenza sul tipo, ignorando il name sorta, risultando in questo modo:

#=> [{:operator=>"OR", :name=>"a string", :status=>true}, 
# {:operator=>"AND", :name=>"z string", :status=>true}, 
# {:operator=>"AND", :name=>"a string", :status=>false}, 
# {:operator=>"OR", :name=>"z string", :status=>false}, 
# {:operator=>"_NOT_PRESENT", :name=>"b string", :status=>true}, 
# {:operator=>"_NOT_PRESENT", :name=>"d string", :status=>true}, 
# {:operator=>"_NOT_PRESENT", :name=>"a string", :status=>false}, 
# {:operator=>"_NOT_PRESENT", :name=>"c string", :status=>false}] 
Problemi correlati