简体   繁体   中英

Ruby select latest duplicated values from an array of hash

Let say I have this kind of array

a = [
    {key: "cat", value: 1},
    {key: "dog", value: 2},
    {key: "mouse", value: 5},
    {key: "rat", value: 3},
    {key: "cat", value: 5},
    {key: "rat", value: 2},
    {key: "cat", value: 1},
    {key: "cat", value: 1}
]

Let say I have this array, and want to get only the latest value found for "cat".

I know how to select all of them

like

a.select do |e|
   e[:key] == "cat"
end

But I'm looking for a way to just get a selection of the last 3

desired result would be

[
    {key: "cat", value: 5},
    {key: "cat", value: 1},
    {key: "cat", value: 1}
]

thanks!

In a comment on the question @Stefan suggested:

a.select { |e| e[:key] == "cat" }.last(3)

Provided a is not too large that is likely what you should use. However, if a is large, and especially if it contains many elements (hashes) h for which h[:key] #=> "cat" , it likely would be more efficient to iterate backwards from the end of the array and terminate ("short-circuit") as soon as three elements h have been found for which h[:key] #=> "cat" . This also avoids the construction of a potentially-large temporary array ( a.select { |e| e[:key] == "cat" } ).


One way to do that is as follows.

a.reverse_each.with_object([]) do |h,arr|
  arr.insert(0,h) if h[:key] == "cat"
  break arr if arr.size == 3
end
  #=> [{:key=>"cat", :value=>5},
  #    {:key=>"cat", :value=>1},
  #    {:key=>"cat", :value=>1}]

See Array#reverse_each , Enumerator#with_object and Array#insert . Note that because reverse_each and with_object both return enumerators, chaining them produces an enumerator as well:

a.reverse_each.with_object([])
  #=> #<Enumerator: #<Enumerator: [{:key=>"cat", :value=>1},
  #   ...
  #   {:key=>"cat", :value=>1}]:reverse_each>:with_object([])>

It might be ever-so-slightly faster to replace the block calculation with

arr << h if h[:key] == "cat"
break arr.reverse if arr.size == 3

If a contains fewer elements h for which h[:key] #=> "cat" an array arr will be returned for which arr.size < 3 . It therefore is necessary to confirm that the array returned contains three elements.

This check must also be performed when @Stefan's suggested code is used, as (for example)

a.select { |e| e[:key] == "cat" }.last(99)
  #=> [{:key=>"cat", :value=>1},
  #    {:key=>"cat", :value=>5},
  #    {:key=>"cat", :value=>1},
  #    {:key=>"cat", :value=>1}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM