2012-06-19 14 views
10

Hej Mam tablicę, w której każdy element jest hash zawierający kilka wartości i liczba.Zdobądź pierwsze n elementów z tablicy ruby ​​wartości mieszania

result = [ 
      {"count" => 3,"name" => "user1"}, 
      {"count" => 10,"name" => "user2"}, 
      {"count" => 10, "user3"}, 
      {"count" => 2, "user4"} 
     ] 

mogę posortować tablicę według liczby w następujący sposób:

result = result.sort_by do |r| 
    r["count"] 
end 

Teraz chcę, aby móc pobierać z najlepszymi wpisy n oparte na liczbie (nie tylko pierwszy (n)) Czy istnieje elegancki sposób to zrobić? Jako przykład, niech n = 1 oczekiwałbym zestawu wyników.

[{"count" => 10,"name" => "user2"}, {"count" => 10, "user3"}] 

ponieważ poprosiłem o wszystkich wpisów z najwyższym wynikiem .. gdybym poprosił o najlepszych 2 najwyższymi wynikami bym dostać

[{"count" => 10,"name" => "user2"}, {"count" => 10, "user3"}, {"count" => 3, "user1"}] 

Odpowiedz

24

Enumerable#group_by na ratunek (jak zwykle):

result.group_by { |r| r["count"] } 
     .sort_by { |k, v| -k } 
     .first(2) 
     .map(&:last) 
     .flatten 

Większość prac jest wykonywana przez group_by. sort_by po prostu układa rzeczy tak, że first(2) odłączy wybrane grupy. Następnie map z last wyodrębni hasze count/name, z którymi zacząłeś, a ostateczny flatten wyczyści dodatkowe pozostałe macierze.

+0

Doskonały. Wielkie dzięki za pomoc. –

2

To rozwiązanie nie jest eleganckie pod względem zwięzłości, ale ma lepszą złożoność czasową. Innymi słowy, powinno działać o wiele szybciej w przypadku bardzo dużej liczby skrótów.

Trzeba będzie zainstalować "algorithms" gem w celu wykorzystania struktury danych Heap:

Heaps stanowią efektywne struktury danych, gdy trzeba znaleźć największe lub najmniejsze elementy w grupie. Ten konkretny rodzaj sterty to optimal, jeśli wartość "n" jest znacznie mniejsza niż całkowita liczba par.

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 
2
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"}] 
+0

tokland a.k.a Arnau Sánchez jest jednym z najlepszych programistów ROR, których znalazłem na SO. :) Twoja odpowiedź pomogła mi uzyskać pożądane wyniki przy użyciu skrótów ruby. Dzięki. – LearningROR

2

Zaczynając w Ruby 2.2.0, max_by bierze dodatkowy argument, który pozwala prosić o pewnej liczby górnych elementów, a nie tylko uzyskanie jeden. Korzystanie z tym, możemy poprawić mu jest zbyt krótki „s odpowiedź

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"}] 

Docs nie mów, jeśli tablica zwrócona przez max_by jest posortowana. Jeśli jednak okaże się to prawdą, możemy po prostu użyć reverse w ostatnim kroku zamiast sortowania.

Powiązane problemy