Как мне получить уникальные элементы из массива хэшей в Ruby?

StackOverflow https://stackoverflow.com/questions/181091

  •  05-07-2019
  •  | 
  •  

Вопрос

У меня есть массив хэшей, и я хочу извлечь из него уникальные значения.Зовущий Array.uniq это не дает мне того, чего я ожидаю.

a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]

Там, где я ожидал:

[{:a => 1}, {:a => 2}]

Порывшись в сети, я не нашел решения, которое меня устраивало бы.Люди рекомендовали пересмотреть определение Hash.eql? и Hash.hash, поскольку это то , что Array.uniq запрашивает.

Редактировать:Там, где я сталкивался с этим в реальном мире, хэши были немного сложнее.Они были результатом синтаксического анализа JSON, который содержал несколько полей, некоторые из значений которых также были хэшами.У меня был массив этих результатов, которые я хотел отфильтровать по уникальным значениям.

Мне не нравится это переопределение Hash.eql? и Hash.hash решение, потому что мне либо пришлось бы переопределить Hash глобально или переопределите его для каждой записи в моем массиве.Изменение определения Hash для каждой записи это было бы громоздко, тем более что внутри каждой записи могут быть вложенные хэши.

Меняющийся Hash глобальный характер имеет определенный потенциал, особенно если бы это было сделано временно.Я бы хотел создать другой класс или вспомогательную функцию, которая включала бы сохранение старых определений и их восстановление, но я думаю, что это добавляет больше сложности, чем действительно необходимо.

Используя inject кажется хорошей альтернативой переопределению Hash.

Это было полезно?

Решение

Я могу получить то, что хочу, позвонив inject

a = [{:a => 1},{:a => 2}, {:a => 1}]
a.inject([]) { |result,h| result << h unless result.include?(h); result }

Это вернет:

[{:a=>1}, {:a=>2}]

Другие советы

Ruby 1.8.7+ вернет именно то, что вы ожидали:

[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}] 

У меня была похожая ситуация, но у хэшей были ключи.Я использовал метод сортировки.

Что я имею в виду:

у вас есть массив:

[{:x=>1},{:x=>2},{:x=>3},{:x=>2},{:x=>1}]

ты сам разбираешься в этом (#sort_by {|t| t[:x]}) и получи это:

[{:x=>1}, {:x=>1}, {:x=>2}, {:x=>2}, {:x=>3}]

теперь немного измененная версия ответа Аарона Хинни:

your_array.inject([]) do |result,item| 
  result << item if !result.last||result.last[:x]!=item[:x]
  result
end

Я тоже пытался:

test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]}

но это происходит очень медленно.вот мой ориентир:

test=[]
1000.times {test<<{:x=>rand}}

Benchmark.bmbm do |bm|
  bm.report("sorting: ") do
    test.sort_by {|t| t[:x]}.inject([]) {|r,h| r<<h if !r.last||r.last[:x]!=h[:x]; r}
  end
  bm.report("inject: ") {test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]} }
end

Результаты:

Rehearsal ---------------------------------------------
sorting:    0.010000   0.000000   0.010000 (  0.005633)
inject:     0.470000   0.140000   0.610000 (  0.621973)
------------------------------------ total: 0.620000sec

                user     system      total        real
sorting:    0.010000   0.000000   0.010000 (  0.003839)
inject:     0.480000   0.130000   0.610000 (  0.612438)

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

a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}

Hash.to_a создает массив массивов ключ-значение, поэтому первая карта дает вам:

[[:a, 1], [:a, 2], [:a, 1]]

uniq в массивах делает то, что вы хотите, давая вам:

[[:a, 1], [:a, 2]]

а затем вторая карта снова объединяет их в виде хэшей.

Вы можете использовать (протестировано в ruby 1.9.3),

[{a: 1},{a: 2},{a:1}].uniq => [{a:1},{a: 2}]
[{a: 1,b: 2},{a: 2, b: 2},{a: 1, b: 3}].uniq_by {|v| v[:a]} => [{a: 1,b: 2},{a: 2, b: 2}]

Ответ, который вы даете, похож на тот, который обсуждался здесь.Это переопределяет hash и eql? методы для хэшей, которые должны появиться в массиве, который затем создает uniq ведите себя правильно.

Метод pipe для массивов (доступен с версии 1.8.6) выполняет объединение множеств (возвращает массив), поэтому ниже приведен еще один возможный способ получения уникальных элементов любого массива a:

[] | a

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top