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 }
Sagar Pandya
  • 9,323
  • 2
  • 24
  • 35
Sri
  • 2,233
  • 4
  • 31
  • 55
  • 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? – spickermann Nov 05 '17 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 – Sri Nov 05 '17 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? – spickermann Nov 05 '17 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. – Sri Nov 05 '17 at 15:19
  • if we have the set of items, adding the `id` into the cart and find the `cost` for the `id` and update the `cost` according to the offer is my primary goal. So I did push all the user selected `id` into the items array which turns to be an array of hashes. – Sri Nov 05 '17 at 15:22
  • 1
    _"I tried with each method"_ – show your attempt, please. – Stefan Nov 05 '17 at 15:41
  • I updated my questions @Stefan – Sri Nov 05 '17 at 15:47

3 Answers3

3

You can achieve this by using each on array

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

Cheers!

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"}]
Stefan
  • 109,145
  • 14
  • 143
  • 218
  • Would you be available to clear one more question, please? – Sri Nov 05 '17 at 16:37
  • Whoa. I didn't know hashes could do that. Why doesn't this happen when you set a primitive variable to another variable? – teddybear Nov 05 '17 at 17:59
  • @teddybear I'm not sure what you mean. Can you give an example? – Stefan Nov 06 '17 at 07:14
  • I got the answer to my question [here](https://stackoverflow.com/questions/47125063/why-does-updating-a-hash-set-to-a-variable-update-that-variable) – teddybear Nov 06 '17 at 17:57
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'
spickermann
  • 100,941
  • 9
  • 101
  • 131