Tutorial :Merging structs with same user id and then sorting based on attributes



Question:

I have any array of structs. Each struct in the array has the following attributes:

  1. user_id
  2. num_hot_dogs_eaten
  3. date_last_pigged_out

Here's what I want to do:

  1. Find the structs with matching user_id's, and merge them into one struct record where num_hot_dogs_eaten is the sum of all matching records and date_last_pigged_out is the most-recent date the user pigged out.

  2. Sort the array of structs by num_hot_dogs_eaten (first order of priority) and by date_last_pigged_out (second order of priority...most-recent first).

  3. Return a new sorted array of structs.


Solution:1

Use this:

def f(users)    r = []    users.each do |u|      new_match = false      match = r.find {|x| x.user_id == u.user_id }      unless match        match = u.dup        r << match        new_match = true      end      match.num_hot_dogs_eaten += u.num_hot_dogs_eaten unless new_match      match.date_last_pigged_out =        [match, u].max_by(&:date_last_pigged_out).date_last_pigged_out    end    r.sort_by {|u| [u.num_hot_dogs_eaten, u.date_last_pigged_out] }.      reverse  end  


Solution:2

A more functional programming approach:

User    = Struct.new(:user_id, :num_hot_dogs_eaten, :date_last_pigged_out)  ONE_DAY = 60 * 60 * 24    class Object    def returning(object)      yield object      object    end  end    users = [    User.new(1, 3, Time.now),    User.new(1, 2, Time.now + ONE_DAY),    User.new(1, 1, Time.now - ONE_DAY),    User.new(2, 2, Time.now - ONE_DAY),    User.new(2, 3, Time.now),    User.new(3, 5, Time.now - ONE_DAY),  ]    users.inject(Hash.new { |hash, key| hash[key] = Hash.new { |hash, key| hash[key] = [] } }) do |collection, user|    returning(collection) do      collection[user.user_id][:num_hot_dogs_eaten]   << user.num_hot_dogs_eaten      collection[user.user_id][:date_last_pigged_out] << user.date_last_pigged_out    end  end.map do |user_id, stats|    User.new(user_id, stats[:num_hot_dogs_eaten].inject(&:+), stats[:date_last_pigged_out].max)  end.sort_by { |user| [user.num_hot_dogs_eaten, user.date_last_pigged_out] }.reverse  

The actual implementation is (assuming you have returning defined):

users.inject(Hash.new { |hash, key| hash[key] = Hash.new { |hash, key| hash[key] = [] } }) do |collection, user|    returning(collection) do      collection[user.user_id][:num_hot_dogs_eaten]   << user.num_hot_dogs_eaten      collection[user.user_id][:date_last_pigged_out] << user.date_last_pigged_out    end  end.map do |user_id, stats|    User.new(user_id, stats[:num_hot_dogs_eaten].inject(&:+), stats[:date_last_pigged_out].max)  end.sort_by { |user| [user.num_hot_dogs_eaten, user.date_last_pigged_out] }.reverse  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »