Reject hash from array of hashes based on a condition in ruby

Viewed 128

I have an array of hashes like below.

[
   {
      "name":"keith",
      "age":"20",
      "weight":"100lb",
      "status":"CURRENT"
   },
   {
      "name":"keith",
      "age":"20",
      "weight":"110lb",
      "status":"PREVIOUS"
   },
   {
      "name":"keith",
      "age":"20",
      "weight":"120lb",
      "status":"FUTURE"
   }
]

I am trying to remove a hash based on the condition that if name and age are the same then fetch out the hash that has the status CURRENT ignoring the other statuses PREVIOUS and FUTURE. so the output should be

[
   {
      "name":"keith",
      "age":"20",
      "weight":"100lb",
      "status":"CURRENT"
   }
]

I have tried using group_by and flat_map but couldn't get the desired output. Could someone please help me in figuring out this?

2 Answers

Try the following:

arr.uniq { |h| [h[:name], h[:age]] }.select { |h| h[:status] == 'CURRENT' }

As far as I understood the problem (including the context added in the comments) the task is as follows:

  • for any combination of (name, age) if we have an entry having status == CURRENT we should return this entry only
  • otherwise, we should return the original entries as is

For example, for the data

data = [
  {
    "name":"keith",
    "age":"20",
    "weight":"100lb",
    "status":"CURRENT"
  },
  {
    "name":"keith",
    "age":"20",
    "weight":"110lb",
    "status":"PREVIOUS"
  },
  {
    "name":"keith",
    "age":"20",
    "weight":"120lb",
    "status":"FUTURE"
  },
  {
    "name":"alice",
    "age":"20",
    "weight":"120lb",
    "status":"PREVIOUS"
  },
  {
    "name":"alice",
    "age":"20",
    "weight":"120lb",
    "status":"FUTURE"
  },
]

the result of the operation described above should be

[
  {:name=>"keith", :age=>"20", :weight=>"100lb", :status=>"CURRENT"},
  {:name=>"alice", :age=>"20", :weight=>"120lb", :status=>"PREVIOUS"},
  {:name=>"alice", :age=>"20", :weight=>"120lb", :status=>"FUTURE"}
]

(so for (keith, 20) we return only a hash where status == CURRENT, for (alive, whatever) we return initial data unchanged).

The solution seems to be very straightforward:

  1. Group by (name, age)
  2. For each group if it contains an entry (hash) with status == CURRENT return just this entry, otherwise return the whole group as is.

Or the same in Ruby:

data
  .group_by { |name:, age:, **| [name, age] }
  .reduce([]) do |acc, (_, group)|
    current = group.find { |status:, **| status == "CURRENT" }    
    current ? acc.push(current) : acc.concat(group)
  end

Related