Seralto
Seralto

Reputation: 1076

Rails - Dealing with array of objects

I have an array of students objects (Student.all) and each object has a name and a class_id.

I want to find students with same class_id and concat their names into the same object, the others remain the same. I mean, turn this:

[
  {
    id: 1,
    name: 'Joe',
    class_id: 55
  },
  {
    id: 2,
    name: 'Bill',
    class_id: 55
  },
  {
    id: 3,
    name: 'Moe',
    class_id: 70
  },
  {
    id: 4,
    name: 'Larry',
    class_id: 80
  },
  {
    id: 5,
    name: 'Phill',
    class_id: 80
  }
]

Into this:

[
  {
    id: 1,
    name: 'Joe/Bill',
    class_id: 55
  },
  {
    id: 3,
    name: 'Moe',
    class_id: 70
  },
  {
    id: 4,
    name: 'Larry/Phill',
    class_id: 80
  }
]

Upvotes: 0

Views: 33

Answers (2)

davidrac
davidrac

Reputation: 10738

Assuming you're only after creating the array (as opposed to updating the DB) you can do something like this:

arr.group_by{|x| x[:class_id]}
   .values.map{|x| x.reduce{|m,v| m[:name] = "#{m[:name]}/#{v[:name]}";m}}

If you care about the original array you can make a small change:

arr.group_by{|x| x[:class_id]}
   .values.map{|x| x[1..-1].reduce(x.first){|m,v| m[:name] = "#{m[:name]}/#{v[:name]}";m}}

The result:

[
    {:id=>1, :name=>"Joe/Joe/Bill",      :class_id=>55},
    {:id=>3, :name=>"Moe",               :class_id=>70},
    {:id=>4, :name=>"Larry/Larry/Phill", :class_id=>80}
]

Upvotes: 3

ifyouseewendy
ifyouseewendy

Reputation: 6674

My solution is to use Enumerable#group_by.

students = [...] # the source array
students.group_by(&:class_id)
# => {
  55 => [
    {
      :id       => 1,
      :name     => "Joe",
      :class_id => 55
    },
    {
      :id       => 2,
      :name     => "Bill",
      :class_id => 55
    }
  ],
  70 => [
    {
      :id       => 3,
      :name     => "Moe",
      :class_id => 70
    }
  ],
  80 => [
    {
      :id       => 4,
      :name     => "Larry",
      :class_id => 80
    },
    {
      :id       => 5,
      :name     => "Phil",
      :class_id => 80
    }
  ]
}

# Code in reduce block may need to be changed to fit your demand
students.group_by(&:class_id).reduce([]) do |ret, (k,v)|
  st = v.first
  st.name = v.map(&:name).join('/')
  ret << st
end
# => target

Upvotes: 2

Related Questions