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