0

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

2 Answers 2

3

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}
]
Sign up to request clarification or add additional context in comments.

2 Comments

This looks great! The power of Ruby is amazing :)
@SérgioToledo not sure what you mean. I added the result to the answer. How is it different from what you're after?
2

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

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.