2009-04-02 17 views
62

Я пытаюсь выяснить, как я могу отфильтровать ключ и значение пары из одного фильтра на другойРуби Hash Whitelist фильтр

Например, я хочу взять эту окрошка

x = { "one" => "one", "two" => "two", "three" => "three"} 

y = x.some_function 

y == { "one" => "one", "two" => "two"} 

Спасибо за вашу помощь

EDIT: возможно, следует упомянуть, что в этом примере я хочу, чтобы он вел себя как белый список. То есть, я знаю, чего хочу, а не то, чего я не хочу.

+0

звучит как дубликат этого: HTTP: // StackOverflow.com/questions/7430343/ruby-easiest-way-to-filter-hash-keys/# answer-30551883 – metakungfu

ответ

48

Возможно, это то, что вы хотите.

wanted_keys = %w[one two] 
x = { "one" => "one", "two" => "two", "three" => "three"} 
x.select { |key,_| wanted_keys.include? key } 

Перечислимый миксин, который включен в, например, Array и Hash предоставляют множество полезных методов, таких как select/reject/each/etc. Я предлагаю вам ознакомиться с документацией для него с ri Enumerable.

+0

, но метод select возвращает массив. В этом случае мне нужен хеш – stellard

+0

А, простите, пропустил этот бит. – sris

+0

Это работает в 1.9.2 сейчас, но все же не в 1.8.7 – stellard

48

Вы можете использовать только встроенную функцию Hash.

x = { "one" => "one", "two" => "two", "three" => "three"} 
y = x.reject {|key,value| key == "three" } 
y == { "one" => "one", "two" => "two"} 

Вы можете поставить любую логику вы хотите в отбраковки, и если блок возвращает истину, он пропустит этот ключ, значение в новой хэш.

+1

5 секунд позади ввода текста ... Я знал, что должен был опубликовать перед тестированием, чтобы убедиться, что я не сделал опечатка. –

+0

Вы можете использовать 'reject!', А не устанавливать другую переменную –

+2

@Radar Разрушительные модификаторы могут вызвать проблемы, если, например, хэш передается как аргумент метода, и вызывающий не ожидает, что хэш будет изменен этим метод. Лучше иметь привычку делать неразрушающие обновления и использовать только деструктивные операторы, когда это необходимо. –

6

Используя комбинацию ответов у всех я пришел к этому решению:

wanted_keys = %w[one two] 
x = { "one" => "one", "two" => "two", "three" => "three"} 
x.reject { |key,_| !wanted_keys.include? key } 
=>{ "one" => "one", "two" => "two"} 

Спасибо за вашу помощь, ребята!

EDIT:

Вышеуказанные работы в 1.8.7+

следующие виды работ в 1.9+:

x.select {| ключ, _ | wanted_keys.include? ключ} ActiveSupport библиотека

+1

Почему двойной отрицательный? 'x.select {| key, _ | wanted_keys.include? ключ} '? Это возвращает хэш для меня –

+0

Это зависит от используемой рубиновой версии. Это работает в 1.9+, но не в 1.8.7. Я отредактирую ответ – stellard

+0

Downvote для принятия вашей собственной копии ответа @ sris! Ваш комментарий к этому ответу достаточно, чтобы указать на разницу. – RobinGower

96

Rails' также дает вам кусочек и для работы с хэш на ключевом уровне, за исключением:

y = x.slice("one", "two") # => { "one" => "one", "two" => "two" } 
y = x.except("three")  # => { "one" => "one", "two" => "two" } 
x.slice!("one", "two") # x is now { "one" => "one", "two" => "two" } 

Это очень приятно, и я использую их все время.

+0

И если x является подклассом ActiveRecord :: Base, вы можете сделать 'y = x.attributes.slice *% w (один два три)'. –

+1

Если вы знакомы с ['_.pick' в Underscore.js] (http://underscorejs.org/#pick), это та же идея. (Я пришел сюда, чтобы найти это!) –

+0

И 'ActiveSupport' довольно легкий, он используется многими драгоценными камнями, не относящимися к Rails. – skalee

6

Улучшение бит @scottd ответ, если вы используете рельсы и имеете список того, что вам нужно, вы можете развернуть список как параметры из среза. Например

hash = { "one" => "one", "two" => "two", "three" => "three"} 
keys_whitelist = %W(one two) 
hash.slice(*keys_whitelist) 

И без рельсов, для любой рубиновой версии, вы можете сделать следующее:

hash = { "one" => "one", "two" => "two", "three" => "three"} 
keys_whitelist = %W(one two) 
Hash[hash.find_all{|k,v| keys_whitelist.include?(k)}] 
+0

Обратите внимание, что вам необязательно использовать * Rails *, вы можете просто загрузить Active Support. Добавьте 'active_support' в ваш Gemfile и' require 'active_support/core_ext/hash/slice "'. – GMA

+0

'find_all' - это псевдоним для' select', так что это в основном то же самое, что и первые два ответа :-) – AlexChaffee

0

Я хотел бы использовать лямбду процедить. Это позволит вам написать сложную логику фильтрации &, что позволяет легко ее протестировать. Тот факт, что логика фильтрации извлечена, позволит повторно использовать ее в других контекстах.

например:

x = { "one" => "one", "two" => "two", "three" => "three"} 

matcher = ->(key,value) { 
    # FILTERING LOGIC HERE 
    !key[/three/] 
} 

x.select(&matcher) == { "one" => "one", "two" => "two"} 
Смежные вопросы