Получить верхние n элементов из массива рубинов хэш-значений
Привет, у меня есть массив, в котором каждый элемент является хешем, содержащим несколько значений и счет.
result = [
{"count" => 3,"name" => "user1"},
{"count" => 10,"name" => "user2"},
{"count" => 10, "user3"},
{"count" => 2, "user4"}
]
Я могу сортировать массив по счету следующим образом:
result = result.sort_by do |r|
r["count"]
end
Теперь я хочу иметь возможность извлекать верхние n записей на основе count (а не только сначала (n)). Есть ли элегантный способ сделать это?
Итак, в качестве примера, пусть n = 1, я ожидал бы набор результатов.
[{"count" => 10,"name" => "user2"}, {"count" => 10, "user3"}]
так как я попросил все записи с наивысшим результатом. Если бы я попросил высшие 2 наивысших оценки, я бы получил
[{"count" => 10,"name" => "user2"}, {"count" => 10, "user3"}, {"count" => 3, "user1"}]
Ответы
Ответ 1
Enumerable#group_by
для спасения (как обычно):
result.group_by { |r| r["count"] }
.sort_by { |k, v| -k }
.first(2)
.map(&:last)
.flatten
Большая часть работы выполняется с помощью group_by
. sort_by
просто строит вещи так, чтобы first(2)
выберет группы, которые вы хотите. Затем map
с last
выберет счетчик /name хэши, с которых вы начали, и окончательный flatten
очистит лишние оставшиеся массивы.
Ответ 2
Это решение не является элегантным с точки зрения краткости, но оно имеет более сложную временную сложность.
Другими словами, он должен выполнять намного быстрее для очень большого количества хэшей.
Вам понадобится установить "алгоритмы" для использования структуры данных кучи:
Heaps - эффективная структура данных, когда вам нужно найти самые большие или самые маленькие элементы в группе. Этот тип кучи оптимальный, если значение "n" намного меньше, чем общее количество пар.
require 'algorithms'
def take_highest(result,n)
max_heap = Containers::Heap.new(result){|x,y| (x["count"] <=> y["count"]) == 1}
last = max_heap.pop
count = 0
highest = [last]
loop do
top = max_heap.pop
break if top.nil?
count += (top["count"] == last["count"] ? 0 : 1)
break if count == n
highest << top
last = top
end
highest
end
Ответ 3
new_result = result.
sort_by { |r| -r["count"] }.
chunk { |r| r["count"] }.
take(2).
flat_map(&:last)
#=> [{"count"=>10, "name"=>"user3"},
# {"count"=>10, "name"=>"user2"},
# {"count"=> 3 "name"=>"user1"}]
Ответ 4
Начиная с Ruby 2.2.0, max_by
принимает дополнительный аргумент, который позволяет вам запрашивать определенное количество верхних элементов, а не просто получая один. Используя это, мы можем улучшить на mu слишком короткий ответ
result = [
{count: 3, name: 'user1'},
{count: 10, name: 'user2'},
{count: 10, name: 'user3'},
{count: 2, name: 'user4'}
]
p result.group_by { |r| r[:count] }
.max_by(2, &:first)
.flat_map(&:last)
.sort_by { |r| -r[:count] }
# => [{:count=>10, :name=>"user2"}, {:count=>10, :name=>"user3"}, {:count=>3, :name=>"user1"}]
Документы не говорят, отсортирован ли массив, возвращаемый max_by
. Если это правда, хотя мы могли бы просто использовать reverse
на последнем шаге вместо сортировки.