2015-11-25 3 views
1

У меня есть массив со следующими элементами:Как группировать массив по одинаковым элементам?

array = [1, 2, 1, 3, 2, nil, 3, 3] 

Я хочу, чтобы сгруппировать их путем сопоставления элементов и присвоить его хэш. Вывод должен выглядеть примерно так:

{ one: [1, 1], two: [2, 2], three: [3, 3, 3], none: [nil] } 

Примечание: array может содержать только 1, 2, 3 и nil элементы.

Ключи (:one, :two, :three, :none) могут быть жестко закодированы.

+0

Зачем вам эта странная и избыточная структура данных? Было бы не лучше, если бы у вас были счета каждого элемента? Например: '{1 => 2, 2 => 1, 3 => 3, nil => 1}' –

+0

В чем вопрос? Какова логика для ключей?Какова логика, определяющая количество элементов в массиве? – sawa

+0

@sawa Это требование генерации хэша, подобного этому. – Arif

ответ

6

Вы можете group_by массив (элементов) по itself:

array = [1, 2, 1, 3, 2, nil, 3, 3] 
hash = array.group_by(&:itself) 
#=> {1=>[1, 1], 2=>[2, 2], 3=>[3, 3, 3], nil=>[nil]} 

После этого, вы можете использовать map для "переименовать" ключи:

keys = { 1 => :one, 2 => :two, 3 => :three, nil => :none } 
hash.map { |k, v| [keys[k], v] }.to_h 
#=> {:one=>[1, 1], :two=>[2, 2], :three=>[3, 3, 3], :none=>[nil]} 
+1

для 'self' ruby ​​2.2+ требуется, чтобы он не работал для старой версии. 1+ для 'self' –

+0

Я использую 2.1.6. Итак, я получаю undefined метод 'self 'для 1: Fixnum – Arif

+2

@Arif просто использовать' group_by {| e | e} 'в этом случае – Stefan

0

Как насчет этого

hash = array.group_by{|e| e} 

[hash].each do |k| 
    k[:one] = k.delete 1 
    k[:two] = k.delete 2 
    k[:three] = k.delete 3 
    k[:none] = k.delete nil 
end 

вы получите свой выход. Если вы не хотите, чтобы петли сделайте следующее

hash[:one] = hash.delete 1 
hash[:two] = hash.delete 2 
hash[:three] = hash.delete 3 
hash[:none] = hash.delete nil 
1

Исходя из фона Perl, я привык сбивающих с толку способов munge массивы и хэши. Вот как это повредило мне:

Использование group_by является очевидным способом найти сходство:

array = [1, 2, 1, 3, 2, nil, 3, 3] 
hash = array.group_by{ |i| i } # => {1=>[1, 1], 2=>[2, 2], 3=>[3, 3, 3], nil=>[nil]} 

Использования хэша является простым способом отображения от одного к другому:

new_keys = { 
    1 => :one, 
    2 => :two, 
    3 => :three, 
    nil => :none 
} 

Здесь влияние Perl влияет на мой разум:

new_keys.values.zip(hash.values_at(*new_keys.keys)).to_h # => {:one=>[1, 1], :two=>[2, 2], :three=>[3, 3, 3], :none=>[nil]} 
new_keys.each_with_object({}){ |(k, v), h| h[v] = hash[k] } # => {:one=>[1, 1], :two=>[2, 2], :three=>[3, 3, 3], :none=>[nil]} 
new_keys.inject({}){ |h, (k, v)| h[v] = hash[k]; h }  # => {:one=>[1, 1], :two=>[2, 2], :three=>[3, 3, 3], :none=>[nil]} 

Поскольку t эй все делают одно и то же, но по-разному, я хотел знать, что было самым быстрым. И, потому что мне нравится @map подхода Стефана я хотел бы видеть, есть ли преимущество в скорости мудра для конкретного метода:

require 'fruity' 

5.times do 

    compare do 
    _zip    { new_keys.values.zip(hash.values_at(*new_keys.keys)).to_h } 
    _each_with_object { new_keys.each_with_object({}){ |(k, v), h| h[v] = hash[k] } } 
    _inject   { new_keys.inject({}){ |h, (k, v)| h[v] = hash[k]; h }  } 
    _map    { hash.map { |k, v| [new_keys[k], v] }.to_h     } 
    end 

    puts 
end 

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

# >> Running each test 2048 times. Test will take about 1 second. 
# >> _each_with_object is similar to _inject 
# >> _inject is faster than _zip by 19.999999999999996% ± 10.0% 
# >> _zip is faster than _map by 10.000000000000009% ± 10.0% 
# >> 
# >> Running each test 2048 times. Test will take about 1 second. 
# >> _each_with_object is similar to _inject 
# >> _inject is similar to _zip 
# >> _zip is similar to _map 
# >> 
# >> Running each test 2048 times. Test will take about 1 second. 
# >> _inject is similar to _each_with_object 
# >> _each_with_object is similar to _zip 
# >> _zip is faster than _map by 10.000000000000009% ± 10.0% 
# >> 
# >> Running each test 2048 times. Test will take about 1 second. 
# >> _each_with_object is similar to _inject 
# >> _inject is faster than _zip by 19.999999999999996% ± 10.0% 
# >> _zip is faster than _map by 10.000000000000009% ± 10.0% 
# >> 
# >> Running each test 2048 times. Test will take about 1 second. 
# >> _each_with_object is similar to _inject 
# >> _inject is faster than _zip by 19.999999999999996% ± 10.0% 
# >> _zip is faster than _map by 10.000000000000009% ± 10.0% 
# >> 
1

Коротким решения с использованием humanize камня для преобразования чисел в слова.

require 'humanize' 
array.group_by { |e| e.humanize.to_sym rescue :none } 
# => {:one=>[1, 1], :two=>[2, 2], :three=>[3, 3, 3], :none=>[nil]} 
Смежные вопросы