Bhawan
Bhawan

Reputation: 2491

Merge a single array into nested array in rails

I have a list of objects something like this:

      a = [
          {
              id: 0,
              name: "ABC",
              work: "ABC2"
          },
          {
              id: 0,
              name: "XYZ",
              work: "XYZ2"
          },
          {
              id: 1,
              name: "ACD",
              work: "ACD2"
          }
      ]

And I want to convert it into something like this:

      b = [
            {
              id: 0,
              fields: [
                  {
                    name: "ABC",
                    work: "ABC2"
                  },
                  {
                    name: "XYZ",
                    work: "XYZ2"
                  }
                ]
            },

            {
                id: 1,
                fields: [
                    {
                        name: "ACD",
                        work: "ACD2"
                    }
                ]
            }
      ]

The idea is to group the objects (by id) in one array.

The approach I tried is:

      b = []

      rest_object = []

      a.each_with_index do |aa, idx|

        aa.delete(:id)

        rest_object << aa

        if idx == 0
          next
        end

        puts a[idx][:id], a[idx-1][:id]

        if a[idx][:id] != a[idx-1][:id]
          b << {id: a[idx-1][:id], names: rest_object}
          rest_object = []
        end

      end

But I am getting an empty output.

Also, if it is possible to achieve the same in some cleaner way. That would be helpful.

Upvotes: 0

Views: 100

Answers (4)

3limin4t0r
3limin4t0r

Reputation: 21130

Something like this does the job. This deletes the :id key-value pair from each hash and uses the value to group the remainder of the hash. Then map the resulting hash to created an array and transform the data into {id: ..., fields: ...} format.

a = [{id: 0, name: "ABC", work: "ABC2"}, {id: 0, name: "XYZ", work: "XYZ2"}, {id: 1, name: "ACD", work: "ACD2"}]

b = a.group_by { |hash| hash.delete(:id) }
     .map { |id, fields| {id: id, fields: fields} }
#=> [{:id=>0, :fields=>[{:name=>"ABC", :work=>"ABC2"}, {:name=>"XYZ", :work=>"XYZ2"}]}, {:id=>1, :fields=>[{:name=>"ACD", :work=>"ACD2"}]}]

Note: This mutates the hashes in the a array. If you don't want this change a.group_by to a.map(&:dup).group_by. Which first duplicates all hashes before doing any mutations.

Upvotes: 1

iGian
iGian

Reputation: 11193

If I understand, given a more general array like this:

a = [ { id: 0, name: "ABC", etc: "01" },
      { id: 0, name: "XYZ", etc: "02" },
      { id: 1, name: "ACD", etc: "11" } ]

The first idea I came up with is doing something like this:

require 'active_support/inflector' # for using pluralize

res = a.group_by{ |h| h[:id] }.values.map do |ary|
        h = Hash.new{ |h,k| h[k] = [] }
        ary.each { |hh| hh.each { |k,v| h[k] << v } }
        h[:id] = h[:id].first
        h.transform_keys do |k|
          unless k == :id
            k.to_s.pluralize.to_sym
          else
            k
          end
        end
      end

res #=> [{:id=>0, :names=>["ABC", "XYZ"], :etcs=>["01", "02"]}, {:id=>1, :names=>["ACD"], :etcs=>["11"]}]

This is not exactly the format you require, but to get that format, just change this line

ary.each { |hh| hh.each { |k,v| h[k] << v } }

to

ary.each { |hh| hh.each { |k,v| h[k] << { k => v } } }

Upvotes: 0

Ashok Damaniya
Ashok Damaniya

Reputation: 303

  a = [
      {
          id: 0,
          name: "ABC"
      },
      {
          id: 0,
          name: "XYZ"
      },
      {
          id: 1,
          name: "ACD"
      }
  ]

ary = []
a.each do|val|
 ary[val[:id]] = {id: val[:id]} unless ary[val[:id]] 
 ary[val[:id]][:names] = [] unless ary[val[:id]][:names]
 ary[val[:id]][:names].push({name: val[:name]})
end

Upvotes: 0

ray
ray

Reputation: 5552

Try following,

required_keys = a[0].except(:id).keys
b = a.group_by { |x| x[:id] }

b = b.inject([]) do |m,(k,v)|
      arr =  { id: k }
      required_keys.each do |key|
        arr[key.to_s.pluralize.to_sym] = v.map { |z| z.slice(key) }
      end
      m << arr
    end

# => [{:id=>0, :names=>[{:name=>"ABC"}, {:name=>"XYZ"}]}, {:id=>1, :names=>[{:name=>"ACD"}]}]

Upvotes: 0

Related Questions