1
\$\begingroup\$

I am trying to create a custom hash from a table. User has many transactions. A transaction has a member_id, company_id, contributions. I am trying to create a custom hash which contains transactions(contributions) happened through a member_id to a company.

    {
    member_id: {
        company: {
            id: "",
            name: ""
        },
        transactions: [
            {
                particulars: "",
                contribution: {
                    contribution1: "",
                    contribution2: ""
                }
            }
        ]
       }
   }

I can simply use

transactions.group(:member_id, :company_id).count 

to get the count, but unable to list the transactions grouped to them(Are there any ways?). So i am grouping transaction using group_by(&:member_id) and looping through the hash to generate a hash to my requirement.

Can any one review the bellow code and suggest better way to do this?

user = User.find_by(id: 123)
return {} unless user

transactions = user.transactions
return {} unless transactions.present?

data = {}
transactions.group_by(&:member_id).each do |key, value|
  transactions = []
  company = {}
  value.each do |record|
    company['id'] = record.company_id
    company['name'] = record.company_name
    particulars = record.particulars
    contribution = {}
    contribution['contribution1'] = record.contribution1
    contribution['contribution2'] = record.contribution2
    details = { "particulars": particulars, "contribution": contribution }
    transactions << details
  end
  data[key] = { "company": company, "transactions": transactions }
end
\$\endgroup\$
  • \$\begingroup\$ @BCdotWEB updated. Thanks. \$\endgroup\$ – Aparichith Mar 10 at 15:10
1
\$\begingroup\$

It sounds like you're trying to group transactions by member_id and company_id, though in the code you posted you are only grouping by member_id and then overwriting the company with each new transactions' company.

If you pass a block to group_by, though, you can group by more than a simple method on the object, for instance you can group by an array of attributes that match, such as:

transactions.group_by { |transaction| [transaction.member_id, transaction.company_id] }

and then you still have to loop through and format the transactions how you want, but you can clean that up as well, by using map and each_with_object:

transactions.
  group_by { |transaction| [transaction.member_id, transaction.company_id] }.
  each_with_object({}) do |((member_id, _), transactions), data|
    data[member_id] = {
      # no need to keep rebuilding the company hash, all transactions belong to
      # the same company
      company: { id: transactions.first.company_id,
                 name: transactions.first.company_name },
      transactions: transactions.map do |transaction|
        { 
          particulars: transaction.particulars,
          contribution: { contribution1: transaction.contribution1,
                          contribution2: transaction.contribution2 }
        }
      end
    }
  end

This can be cleaned up even more, by moving the generation of these hashes to presenters but that's just moving the code around at this point, regardless you still need to go through all the transactions and present them as the hash, so the loop is unavoidable.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.