0

I have an array of hashes like below:

items = [ {"id" => 1, "cost" => '2.00'}, 
          {"id" => 2, "cost" => '6.00'}, 
          {"id" => 1, "cost" => '2.00'},
          {"id" => 1, "cost" => '2.00'}, 
          {"id" => 1, "cost" => '2.00'} ]

I would like to update the cost to '8.00' where the id = 1. I have tried with the each method like below which does work but I would like to know if there is another more efficient way of updating the values?

items.each { |h| h["cost"] = "8.00" if h["id"] == 1 }
7
  • Searching a record in an array takes n (n => the number of elements in the array) steps in the worst case. A more efficient way would be a different data structure - for example a hash with the id being the key and the value being the cost. That would take exactly one step to find the element to update. Is using a hash instead of an array with nested hashes an option? Commented Nov 5, 2017 at 15:09
  • Thanks, @spickermann As you said I want to avoid the N + 1 each method and I would like to know any method which should be efficient. So please feel free use a hash Commented Nov 5, 2017 at 15:13
  • There are duplicates in your data structure. Do you need them? If yes, why if you update all to the same costs anyway? Would it be enough to have just one entry per id? Or one entry that stores the number of entries and just one cost? Commented Nov 5, 2017 at 15:16
  • Yes, I need the duplicates because I want to count the total cost. Moreover, the items are basically like a shopping cart. So I want to calculate the total cost. Commented Nov 5, 2017 at 15:19
  • 1
    "I tried with each method" – show your attempt, please. Commented Nov 5, 2017 at 15:41

3 Answers 3

3

You could just use the same object:

item_1 = {'id' => 1, 'cost' => '2.00'}
item_2 = {'id' => 2, 'cost' => '6.00'}

items = [item_1, item_2, item_1, item_1, item_1]
#=> [{"id"=>1, "cost"=>"2.00"}, {"id"=>2, "cost"=>"6.00"},
#    {"id"=>1, "cost"=>"2.00"}, {"id"=>1, "cost"=>"2.00"},
#    {"id"=>1, "cost"=>"2.00"}]

This makes updates trivial:

item_1['cost'] = '8.00'

items
#=> [{"id"=>1, "cost"=>"8.00"}, {"id"=>2, "cost"=>"6.00"},
#    {"id"=>1, "cost"=>"8.00"}, {"id"=>1, "cost"=>"8.00"},
#    {"id"=>1, "cost"=>"8.00"}]
Sign up to request clarification or add additional context in comments.

4 Comments

Would you be available to clear one more question, please?
Whoa. I didn't know hashes could do that. Why doesn't this happen when you set a primitive variable to another variable?
@teddybear I'm not sure what you mean. Can you give an example?
I got the answer to my question here
3

You can achieve this by using each on array

items.each{|v| v["cost"] = "8.00" if v["id"] == 1 }

Cheers!

4 Comments

Just use each.
v is a strange choice for the name of the block variable. hash or h would be more appropriate.
@Stefan Thanks, Does .each is better than @jaspreet answer?
@Vinay you would use map if you were interested in its return value.
2

You might consider changing your data structure from:

items = [{"id" => 1, "cost" => '2.00'}, {"id" => 2, "cost" => '6.00'}, 
         {"id" => 1, "cost" => '2.00'}, {"id" => 1, "cost" => '2.00'}, 
         {"id" => 1, "cost" => '2.00'}]

To a hash like this:

items = { 1 => '2.00', 2 => '6.00' }

To updating the record with id = 1 to 8.00 call:

items[1] = '8.00'

Or if you need to know the number of items, you might want to conside a structure like this:

items = { 1 => ['2.00', 4], 2 => ['6.00', 1] }

Than update like this:

items[1][0] = '8.00'

1 Comment

Indeed. What's more, I'd keep cost as cents with integers.

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.