1

Say I have an array of hashes like so

default_search_order = [
    { field: 'subscribers.nickname',   direction: 'ASC' },
    { field: 'subscribers.email',      direction: 'ASC' },
    { field: 'roles.name',             direction: 'ASC' },
    { field: 'subscribers.first_name', direction: 'ASC' }
]

and I have another array of hashes provided by a query like this:

order = [
    { :field => "subscribers.nickname",  :direction => "DESC"},
    { :field => "subscribers.email",     :direction => "DESC"},
    { :field => "subscribers.last_name", :direction => "DESC"}
]

I want to filter out hashes from default_search_order that have matching values for field and then combine the two arrays of hashes into one array of hashes like so.

correct = [
    { field: "subscribers.nickname",   direction: "DESC"},
    { field: "subscribers.email",      direction: "DESC"},
    { field: "subscribers.last_name",  direction: "DESC"},
    { field: "roles.name",             direction: "ASC" },
    { field: "subscribers.first_name", direction: "ASC" }
]

How would I do that in Ruby on Rails? I've been trying #.delete_if? and #.keep_if? and #.select but I'm going in circles. Help?

Chandan
  • 11,465
  • 1
  • 6
  • 25
Josh Wren
  • 338
  • 2
  • 12

2 Answers2

2

Another solution would be to merge both Arrays and extract unique values

correct = (order + default_search_order).uniq { |f| f[:field] }
correct = order.push(*default_search_order).uniq { |f| f[:field] }

Note: Here variable order matters while merging arrays if default_search_order is written before order variable then result would be different.

Chandan
  • 11,465
  • 1
  • 6
  • 25
0

Here is how I would do it. Step 1 is to index the default_search_order by field - turning it from an array into a hash:

indexed = default_search_order.index_by { |hsh| hsh[:field] }

Now you have a data structure like this:

{
  'subscribers.nickname'   => { field: 'subscribers.nickname',   direction: 'ASC' },
  'subscribers.email'      => { field: 'subscribers.email',      direction: 'ASC' },
  'roles.name'             => { field: 'roles.name',             direction: 'ASC' },
  'subscribers.first_name' => { field: 'subscribers.first_name', direction: 'ASC' }
}

By doing this, we've made our work easy, because we can immediately know which entry in the first list corresponds to a given field in the second, and update it (or set it if it doesn't exist yet).

order.each do |hsh|
  indexed[hsh[:field]] = hsh
end

Finally, we can turn it back into an array by removing our temporary "indexes" (hash keys)

results = indexed.values

Another way to do it would be similar to the first, but a little more concise:

[default_search_order, order]
  .map { |list| list.index_by { |hsh| hsh[:name] } }
  .reduce(&:merge)
  .values
Chandan
  • 11,465
  • 1
  • 6
  • 25
max pleaner
  • 26,189
  • 9
  • 66
  • 118