2013-05-22 1 views
5

Per esempio:Come restituire vero se solo una chiave specifica in un hash ha un vero valore (tutti gli altri valori sono false)

options = { fight: true, 
use_item: false, 
run_away: false, 
save_game: false } 

Voglio un espressione booleana che restituisce true se e solo se solo :fight è true e il resto sono false (come illustrato sopra).

Posso incidere questo insieme, ma sto cercando di allenarmi a scrivere più elegante rubino. Grazie!

EDIT: L'essere mod:

(options[:fight] == true && options.delete(:fight).values.all {|x| !x})

risposta

8

Ispirato dalla risposta di Vitaliy:

options[:flight] && options.values.one? 
+2

come se non distruggesse l'hash –

+0

A volte aiuta solo a pensare logicamente che cosa stai cercando di realizzare. Ottimo approccio +1. – squiguy

1
options.find_all{|k,v| v } == [[:fight, true]] 

o

options.values.count(true) == 1 && options[:fight] 
+0

Non esattamente "più elegante" rispetto l'hack che stanno già utilizzando, IMHO. –

+0

semanticamente uguale a OP –

+0

oh, mi è piaciuto il conteggio (vero) migliore, anche se questo è interessante. –

9

Assumendo tutti i valori sono rigorosamente booleano, è semplice come:

options == {fight: true, use_item: false, run_away: false, save_game: false} 

See documentation for the == method

+0

Sì, lo so come fare, ma c'è una soluzione generale? Potrebbero esserci più opzioni passate e voglio tornare vero solo se combatti: è vero e il resto è falso. –

+1

Non riesco a trovare nulla di più succinto della risposta di @Shawn Balestracci per questo. – DanSingerman

+0

Per quanto ho capito, non dovrebbero essere ignorati, ma dovrebbero essere tutti 'falso' – tessi

2

Penso che il tuo trucco non sia male. Esso può essere semplificato un po 'però:

options.delete(:flight) && options.values.none? 
+0

+1 Per l'eleganza. Tuttavia, restituirà anche true se ': flight' è _thingthing_ diverso da' nil' o 'false'. –

+0

Inoltre distrugge l'hash delle opzioni (anche se potrebbe non essere importante in questo caso) – tessi

1

ne dite:

options.all? {|k,v| k == :fight ? v : !v} 

Per un approccio più generale:

def is_action?(options, action) 
    options.all? {|k,v| k == action ? v : !v} 
end 

is_action? options, :fight 
# => true 
1

Questo è indipendente dal numero di chiave/elems in l'hash.

options[:fight] && options.find_all{|arr| !arr[1]}.size == options.size-1 

Anche Solo un consiglio, in Ruby, non è necessario scrivere qualcosa come:

options[:fight] == true 
0
options.select{ |k, v| v } == [[:fight, true]] 
0

Se controlli il contenuto dell'hash, ed è relativamente piccolo, vorrei usare qualcosa di simile in un metodo privato come soluzione generica.

def exclusively_true?(hash, key) 
    return false unless hash.delete(key) == true 
    !hash.has_value? true 
end 

require 'test/unit' 
class TestExclusive < Test::Unit::TestCase 
    def setup 
    @test_hash = {foo: true, bar: false, hoge: false} 
    end 
    def test_exclusive 
    assert_equal(true, exclusively_true?(@test_hash, :foo)) 
    end 
    def test_inexclusive 
    @test_hash[:bar] = true 
    assert_equal(false, exclusively_true?(@test_hash, :foo)) 
    end 
end 

require 'benchmark' 

h = {foo: true} 
999.times {|i| h["a#{i}"] = false} 
Benchmark.bmbm(30) do |x| 
    x.report('exclusively_true') do 
    1000.times do 
     exclusively_true?(h, :foo) 
    end 
    end 
end 

benchmark artificiosa: (OS X 10.8.3/3 GHz/8 GB)

ruby -v: ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-darwin12.3.0] 
Rehearsal ------------------------------------------------------------------ 
exclusively_true     0.000000 0.000000 0.000000 ( 0.000412) 
--------------------------------------------------------- total: 0.000000sec 

            user  system  total  real 
exclusively_true     0.000000 0.000000 0.000000 ( 0.000331) 
+0

puoi renderlo più breve con "hash.delete (chiave) == true &&! Hash.has_value? (True)" ma stabilire il caso di rifiuto in anticipo aiuterà i futuri sviluppatori a leggere il tuo codice. La leggibilità e l'ottimizzazione sono molto più importanti di quelle intelligenti. – randym