Ruby Hash value traversal and changing value is not working - ruby-on-rails

a = {"a" => 100, "b" => 200, "c" => 300}
b = a.map{|k,v| v = v + 10}
is returning an array, i need to change the values of a hash by call by reference
I am expecting the following output
{"a" => 110, "b" => 210, "c" => 310}
Thanks

Here's my non-mutating one-liner :P
Hash[original_hash.map { |k,v| [k, v+10] }]
Gotta love ruby one-liners :)

Maybe you can do something like this:
a.keys.each do |key| a[key] += 10 end

a.each_pair do |x,y| a[x] += 10 end

Reality check:
require "benchmark"
include Benchmark
h0, h1, h2, h3, h4 = (0..4).map { Hash[(0..1000).map{ |i| [i,i] }] }
bm do |x|
x.report("0") { 1000.times { h0.each_key{ |k| h0[k] += 10 } } }
x.report("1") { 1000.times { h1.keys.each{ |k| h1[k] += 10 } } }
x.report("2") { 1000.times { Hash[h2.map { |k,v| [k, v+10] }] } }
x.report("3") { 1000.times { h3.inject({}){ |h,(k,v)| h[k] = v + 10; h } } }
x.report("4") { 1000.times { h4.inject({}){ |h,(k,v)| h.update( k => v + 10) } } }
end
user system total real
0 0.490000 0.000000 0.490000 ( 0.540795)
1 0.490000 0.010000 0.500000 ( 0.545050)
2 1.210000 0.010000 1.220000 ( 1.388739)
3 1.570000 0.010000 1.580000 ( 1.660317)
4 2.460000 0.010000 2.470000 ( 3.057287)
Imperative programming wins.

Dude change the map with each and you are good to go :)

I believe in every Ruby question inject should be presented :D
b = a.inject({}){ |h,(k,v)| h[k] = v + 10; h }
#=> {"a"=>110, "b"=>210, "c"=>310}

Related

Generating a tree from nested set

I am looking to display a html tree from nested set data.
As the displayed tree does not always display all branches the leaf nodes cannot be identified by subtracting the lft & rgt values.
1 Drinks 27
/ | \
2 Coffee 3 4 Tea 20 21 Milk 26
/ \
5 Black 8 9 Green 19
/ \
10 China 14 15 Africa 18
I was looking to adapt the following code:
How to render all records from a nested set into a real html tree
Solution:
Happy to receive code improvement suggestions :)
def tree_from_set(set, start_level)
buf = "<ul>"
parent = []
prev = set[start_level]
set[start_level+1..-1].each do |node|
if node.lft.between?(prev.lft, prev.rgt)
# Previous was the parent
buf << open_parent_tag(prev)
parent.push(prev)
else
if node.lft.between?(parent.last.lft, parent.last.rgt)
#Previous was a child
buf << leaf_tag(prev)
else
buf << leaf_tag(prev)
begin
buf << "</ul></li>"
parent.pop
end until parent.empty? or node.lft.between?(parent.last.lft, parent.last.rgt)
end
end
prev = node
end
buf << leaf_tag(prev)
begin
buf << "</ul></li>"
parent.pop
end until parent.empty?
buf << "</ul>"
buf.html_safe
end
def open_parent_tag(node)
%{ <li>
#{link_to(node.name, node)}
<ul>
}
end
def leaf_tag(node)
content_tag(:li, link_to(node.name, node))
end
I used this function, but on php, not on ruby:
<?php
//nested sets data ordered by left
$data = array(
array("left" => 1, "right" => 10, "name" => "P0"),
array("left" => 2, "right" => 7, "name" => "P1"),
array("left" => 3, "right" => 4, "name" => "P11"),
array("left" => 5, "right" => 6, "name" => "P12"),
array("left" => 8, "right" => 9, "name" => "P2")
);
//Converter function gets nested sets array and returns nested php array
function nest($arrData){
$stack = array();
$arraySet = array();
foreach( $arrData as $intKey=>$arrValues) {
$stackSize = count($stack);
while($stackSize > 0 && $stack[$stackSize-1]['right'] < $arrValues['left']) {
array_pop($stack);
$stackSize--;
}
$link =& $arraySet;
for($i=0;$i<$stackSize;$i++) {
$link =& $link[$stack[$i]['id']]["children"]; //navigate to the proper children array
}
$tmp = array_push($link, array ('item'=>$arrValues,'children'=>array()));
array_push($stack, array('id' => $tmp-1, 'right' => $arrValues['right']));
}
return $arraySet;
}
//Print result
printArray(nest($data));
function printArray($array){
echo "<ul>";
foreach ($array as $row){
$children = $row['children'];
echo "<li>";
echo $row['item']['name'];
if (!empty($children)) printArray($children);
echo "</li>";
}
echo "</ul>";
}
?>
Managed to convert it to C++ for those who was looking for it like I was.
readDataBaseData(QVector<NodeData> &data, DBNode *node)
{
if(data.size() < 1){
return;
}
QVector<QPair<NodeData, int>> stack;
DBNode* arrayRes = node;
foreach (NodeData curArrDataItem, data) {
int stackSize = stack.size();
while(stackSize > 0 &&
stack.at(stackSize - 1).first.right < curArrDataItem.left){
stack.pop_back();
stackSize--;
}
DBNode* link = arrayRes;
for(int i = 0; i < stackSize; i++){
link = link->childAt(stack.at(i).second);
}
link = new DBNode(curArrDataItem, link);
stack.push_back(QPair<NodeData, int>(curArrDataItem, link->getParent()->childCount() - 1));
}
}

Sum previous value in hash

Basically I´ve got a hash and I would like to sum the current value with the previous.
i.e
what I have
hash = {:a=>5, :b=>10, :c=>15, :d=>3}
The result that I want
{:a=>5, :b=>15, :c=>30, :d=>33}
hash.inject(0) { |s, (k, v)| hash[k] = s + v }
# => 33
hash
# => {:a=>5, :b=>15, :c=>30, :d=>33}
If you want to preserve the original hash, you can use each_with_object instead:
hash.each_with_object({}) { |(k, v), h| h[k] = v + (h.values.last||0) }
# => {:a=>5, :b=>15, :c=>30, :d=>33}
The following will return a new hash instance:
hash.each_with_object({}) { |(key, val), new_hash| new_hash[key] = val + (new_hash.values.last||0) }

Can I iterate through an array during a comparison?

s = Array.new
s << 19
while (s.last + 19) < 100000 do
s << s.last + 19
end
This^ works. s is an array of all factors of 19 below 100,000.
I'm trying to, in a succinct statement, find all numbers in s where the reverse of that number is also in the array. Ex: 176 and 671.
reflections= s.select { |num| num.to_s.reverse == s.each.to_s }
I know this is wrong, but how can I check each reversed item against the entire array?
This should work:
reflections = s.select { |num| s.include?(num.to_s.reverse.to_i) }
Although it produces results that you probably didn't anticipate
s = [176, 234, 671, 111]
reflections = s.select { |num| s.include?(num.to_s.reverse.to_i) }
reflections # => [176, 671, 111]
These are all valid results according to your logic.
Excluding self-match is pretty straighforward:
s = [176, 234, 671, 111]
reflections = s.select do |x|
x = x.to_s
r = x.reverse
(x != r) && s.include?(r.to_i)
end
reflections # => [176, 671]
reflections = s & s.map{|num| num.to_s.reverse.to_i}
Try:
reverse_array = s.select {|num| num.to_s == num.to_s.reverse }
UPDATE:
After checking I found this will work:
myarr = ""
s = (1..1000)
s.select{ |num|
unless s.include?(num.to_s.reverse.to_i)
myarr << num.to_s
end
}
Finally, the myarr will contain all the numbers whose reverse is present in array s.

Rails mapping array of hashes onto single hash

I have an array of hashes like so:
[{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
And I'm trying to map this onto single hash like this:
{"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}
I have achieved it using
par={}
mitem["params"].each { |h| h.each {|k,v| par[k]=v} }
But I was wondering if it's possible to do this in a more idiomatic way (preferably without using a local variable).
How can I do this?
You could compose Enumerable#reduce and Hash#merge to accomplish what you want.
input = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
input.reduce({}, :merge)
is {"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}
Reducing an array sort of like sticking a method call between each element of it.
For example [1, 2, 3].reduce(0, :+) is like saying 0 + 1 + 2 + 3 and gives 6.
In our case we do something similar, but with the merge function, which merges two hashes.
[{:a => 1}, {:b => 2}, {:c => 3}].reduce({}, :merge)
is {}.merge({:a => 1}.merge({:b => 2}.merge({:c => 3})))
is {:a => 1, :b => 2, :c => 3}
How about:
h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)
Every answers until now are advising to use Enumerable#reduce (or inject which is an alias) + Hash#merge but beware, while being clean, concise and human readable this solution will be hugely time consuming and have a large memory footprint on large arrays.
I have compiled different solutions and benchmarked them.
Some options
a = [{'a' => {'x' => 1}}, {'b' => {'x' => 2}}]
# to_h
a.to_h { |h| [h.keys.first, h.values.first] }
# each_with_object
a.each_with_object({}) { |x, h| h.store(x.keys.first, x.values.first) }
# each_with_object (nested)
a.each_with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } }
# map.with_object
a.map.with_object({}) { |x, h| h.store(x.keys.first, x.values.first) }
# map.with_object (nested)
a.map.with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } }
# reduce + merge
a.reduce(:merge) # take wayyyyyy to much time on large arrays because Hash#merge creates a new hash on each iteration
# reduce + merge!
a.reduce(:merge!) # will modify a in an unexpected way
Benchmark script
It's important to use bmbm and not bm to avoid differences are due to the cost of memory allocation and garbage collection.
require 'benchmark'
a = (1..50_000).map { |x| { "a#{x}" => { 'x' => x } } }
Benchmark.bmbm do |x|
x.report('to_h:') { a.to_h { |h| [h.keys.first, h.values.first] } }
x.report('each_with_object:') { a.each_with_object({}) { |x, h| h.store(x.keys.first, x.values.first) } }
x.report('each_with_object (nested):') { a.each_with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } } }
x.report('map.with_object:') { a.map.with_object({}) { |x, h| h.store(x.keys.first, x.values.first) } }
x.report('map.with_object (nested):') { a.map.with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } } }
x.report('reduce + merge:') { a.reduce(:merge) }
x.report('reduce + merge!:') { a.reduce(:merge!) }
end
Note: I initially tested with a 1_000_000 items array but as reduce + merge is costing exponentially much time it will take to much time to end.
Benchmark results
50k items array
Rehearsal --------------------------------------------------------------
to_h: 0.031464 0.004003 0.035467 ( 0.035644)
each_with_object: 0.018782 0.003025 0.021807 ( 0.021978)
each_with_object (nested): 0.018848 0.000000 0.018848 ( 0.018973)
map.with_object: 0.022634 0.000000 0.022634 ( 0.022777)
map.with_object (nested): 0.020958 0.000222 0.021180 ( 0.021325)
reduce + merge: 9.409533 0.222870 9.632403 ( 9.713789)
reduce + merge!: 0.008547 0.000000 0.008547 ( 0.008627)
----------------------------------------------------- total: 9.760886sec
user system total real
to_h: 0.019744 0.000000 0.019744 ( 0.019851)
each_with_object: 0.018324 0.000000 0.018324 ( 0.018395)
each_with_object (nested): 0.029053 0.000000 0.029053 ( 0.029251)
map.with_object: 0.021635 0.000000 0.021635 ( 0.021782)
map.with_object (nested): 0.028842 0.000005 0.028847 ( 0.029046)
reduce + merge: 17.331742 6.387505 23.719247 ( 23.925125)
reduce + merge!: 0.008255 0.000395 0.008650 ( 0.008681)
2M items array (excluding reduce + merge)
Rehearsal --------------------------------------------------------------
to_h: 2.036005 0.062571 2.098576 ( 2.116110)
each_with_object: 1.241308 0.023036 1.264344 ( 1.273338)
each_with_object (nested): 1.126841 0.039636 1.166477 ( 1.173382)
map.with_object: 2.208696 0.026286 2.234982 ( 2.252559)
map.with_object (nested): 1.238949 0.023128 1.262077 ( 1.270945)
reduce + merge!: 0.777382 0.013279 0.790661 ( 0.797180)
----------------------------------------------------- total: 8.817117sec
user system total real
to_h: 1.237030 0.000000 1.237030 ( 1.247476)
each_with_object: 1.361288 0.016369 1.377657 ( 1.388984)
each_with_object (nested): 1.765759 0.000000 1.765759 ( 1.776274)
map.with_object: 1.439949 0.029580 1.469529 ( 1.481832)
map.with_object (nested): 2.016688 0.019809 2.036497 ( 2.051029)
reduce + merge!: 0.788528 0.000000 0.788528 ( 0.794186)
Use #inject
hashes = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
merged = hashes.inject({}) { |aggregate, hash| aggregate.merge hash }
merged # => {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Here you can use either inject or reduce from Enumerable class as both of them are aliases of each other so there is no performance benefit to either.
sample = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
result1 = sample.reduce(:merge)
# {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
result2 = sample.inject(:merge)
# {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

Regular expression in ruby and matching so many results

Trying to create a simple regular expression that can extract numbers(between 7 - 14) after a keyword starting with g letter and some id, something like following :
(g)(\d{1,6})\s+(\d{7,14}\s*)+
Lets assume :
m = (/(g)(\d{1,6})\s+(\d{7,14}\s*)+/i.match("g12 327638474 83873478 2387327683 44 437643673476"))
I have results of :
#<MatchData "g23333 327638474 83873478 2387327683 " "g" "12" "2387327683 ">
But what I need as a final result , to include, 327638474, 83873478, 2387327683 and exclude 44.
For now I just getting the last number 2387327683 with not including the previous numbers
Any help here .
cheers
Instead of a regex, you can use something like that:
s = "g12 327638474 83873478 2387327683 44 437643673476"
s.split[1..-1].select { |x| (7..14).include?(x.size) }.map(&:to_i)
# => [327638474, 83873478, 2387327683, 437643673476]
Just as a FYI, here is a benchmark showing a bit faster way of accomplishing the selected answer:
require 'ap'
require 'benchmark'
n = 100_000
s = "g12 327638474 83873478 2387327683 44 437643673476"
ap s.split[1..-1].select { |x| (7..14).include? x.size }.map(&:to_i)
ap s.split[1..-1].select { |x| 7 <= x.size && x.size <= 14 }.map(&:to_i)
Benchmark.bm(11) do |b|
b.report('include?' ) { n.times{ s.split[1..-1].select { |x| (7..14).include? x.size }.map(&:to_i) } }
b.report('conditional') { n.times{ s.split[1..-1].select { |x| 7 <= x.size && x.size <= 14 }.map(&:to_i) } }
end
ruby ~/Desktop/test.rb
[
[0] 327638474,
[1] 83873478,
[2] 2387327683,
[3] 437643673476
]
[
[0] 327638474,
[1] 83873478,
[2] 2387327683,
[3] 437643673476
]
user system total real
include? 1.010000 0.000000 1.010000 ( 1.011725)
conditional 0.830000 0.000000 0.830000 ( 0.825746)
For speed I'll use the conditional test. It's a tiny bit more verbose, but is still easily read.

Resources