Home > Software design >  How to organize a list of todos by category using arrays in Ruby?
How to organize a list of todos by category using arrays in Ruby?

Time:10-11

Print the array elements by category using Ruby(avoid direct methods. need deep explanation for my understanding)

here I have the todos array of arrays:

todos = [
      ["Send invoice", "money"],
      ["Clean room", "organize"],
      ["Pay rent", "money"],
      ["Arrange books", "organize"],
      ["Pay taxes", "money"],
      ["Buy groceries", "food"]
    ]

I should first build an array just for categories which looks like this:

[["money", ["Send invoice", "Pay rent", "Pay taxes"]], ...]

Expected output:

  money:
     Send invoice
     Pay rent
     Pay taxes
   organize:
     Clean room
     Arrange books
   food:
     Buy groceries

Here is my try:

    a, b, c = ["money"], ["organize"], ["food"]

for i in todos  
    if i[1]  ==  "money"
            arr = []
        a.push(arr.push(i[0]))  #=> ["money", ["Send invoice"], ["Pay rent"], ["Pay taxes"]]
    elsif i[1] == "organize"
            arr = []
        b.push(arr.push(i[0]))  #=> ["organize", ["Clean room"], ["Arrange books"]]
    else
            arr = []
        c.push(arr.push(i[0])). #=> ["food", ["Buy groceries"]]
    end 
end

Explanation: Here on my code I have directly assigned values to the array a, b, c = ["money"], ["organize"], ["food"] which was wrong. Kindly show me some efficient way to get the expected output.

CodePudding user response:

I would do:

todos = [
  ["Send invoice", "money"],
  ["Clean room", "organize"],
  ["Pay rent", "money"],
  ["Arrange books", "organize"],
  ["Pay taxes", "money"],
  ["Buy groceries", "food"]
]

todos.group_by(&:last).map { |key, values| [key, values.map(&:first)] }
#=> [["money", ["Send invoice", "Pay rent", "Pay taxes"]], ["organize", ["Clean room", "Arrange books"]], ["food", ["Buy groceries"]]]
    

How does this work? Enumerable#group_by returns the nested array grouped by their Array#last value and will return a nested hash structure like this:

{
  "money"=>[["Send invoice", "money"], ["Pay rent", "money"], ["Pay taxes", "money"]],
  "organize"=>[["Clean room", "organize"], ["Arrange books", "organize"]],
  "food"=>[["Buy groceries", "food"]]
}

In the next step of the method chain, it uses Enumerable#map to translate each key/value pair to the final structure by returning an array with the key from the grouped_by and a nested array containing only the first values from the original values. For example, it will take this input in one of the iterations and translate it to:

{ "organize"=>[["Clean room", "organize"], ["Arrange books", "organize"]] }
# with `key` being "organize", and `values` being the nested arrays on the right
# getting translated into:
#=> ["organize", ["Clean room", "Arrange books"]]

CodePudding user response:

We can use #each_with_object to iterate over todos, building a hash, which seems a much more useful data structure for this task.

h = todos.each_with_object({}) do |arr, h|
  task, category = arr 
  h[category] ||= []
  h[category] << task 
end
# => {"money"=>["Send invoice", "Pay rent", "Pay taxes"], 
#     "organize"=>["Clean room", "Arrange books"], 
#     "food"=>["Buy groceries"]}

Doing this is equivalent to:

h = {}

for arr in todos
  task = arr.first
  category = arr.last

  h[category] = [] unless h.has_key? category
  h[category].push(task)
end

To turn this into the array of arrays...

h.map { |k, v| [k, v] }
# => [["money", ["Send invoice", "Pay rent", "Pay taxes"]], 
#     ["organize", ["Clean room", "Arrange books"]], 
#     ["food", ["Buy groceries"]]]
  • Related