0

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.

4 Answers 4

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

To run this in irb surround the whole thing with a begin/end block, or move the dot before map to the end of the group_by line.
0

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

5 Comments

Right now, it is taking only names field in slice. I want every field other than id. There are around 30-40 fields.
Sorry if I was not clear, but I meant this. :names=>[{:name=>"ABC", :work=>"ABC"}, {:name=>"XYZ", :work=>"XYZ"}]
can you provide elaborated input and output example? @Bhawan
Use this if you want all data inside names a.group_by { |x| x[:id] }.inject([]) { |m,(k,v)| m << { id: k, names: v.map { |z| z.slice(*(z.keys-[:id])) } } }
@rahulmishra Great, It is real headache when question is not clear with appropriate example :P
0
  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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.