Ruby merge hashes with nil values - ruby

I'm trying to merge the values from 2 hashes, creating a new one. I've tried with
Hash[b.map{|k,v| [a[k],v]}
but when it finds out that an "a" value is empty(nil) it doesn't print b[k]...I've got something like that:
| A | A | | B | B | ====> | C | C |
| key|value| | key|value| ====> |B_value|A_value|
| key|value| | key|value| ====> |B_value|A_value|
| key| nil | | key|value| ====> MISSING
| key|value| | key|value| ====> |B_value|A_value|
The keys are the same
I need to see also the nil.
If I try to print in array format I can see everything (nil included):
p = a.map{|k,v| [b[k],v]}
Probably map is not the right solution, there's something else that can give me the same result?
This is my code:
header_hostname = Hash.new
working_host = Hash.new
fileset.each do |file|
header = YAML.load_file("output/#{file}")
header.each do |k_header,v_header|
if v_header == "Hostname"
header_hostname = header
end
end
working_host = Hash[header.map{|k, v| [header_hostname[k], v] }]
puts working_host
File.open("tmp/working_hosts.txt","a+") << working_host
the output from Hash is like:
...
Erogazione VlanID: '2390'
" SubnetorIP": 10.*.*.*
" Netmask": 255.255.255.240
" Gateway": 10.*.*.*
...
Backup VlanID: ''
Managment VlanID: ''
Privata HB VlanID: ''
Remote Console VlanID: ''
...
Hashes
Header = {"98"=>"Erogazione VlanID", "99"=>" SubnetorIP", "100"=>" Netmask", "101"=>" Gateway", "102"=>" Speed(f,g)", "103"=>" Bond(s/n)", "104"=>" Porte", "105"=>" Switch", "106"=>" Slot/Porte", "107"=>" PortePPanel", "108"=>" PortePPanel(bond)", "109"=>"Backup VlanID", "110"=>" SubnetorIP", "111"=>" Netmask", "112"=>" Gateway", "113"=>" Speed(f,g)", "114"=>" Porte", "115"=>" Switch", "116"=>" Slot/Porte", "117"=>" PortePPanel", "135"=>"Remote Console VlanID", "136"=>" SubnetorIP", "137"=>" Netmask", "138"=>" Gateway", "139"=>" Speed(f,g)", "140"=>" Porte", "141"=>" Switch", "142"=>" Slot/Porte", "143"=>" PortePPanel"}
Machine1 = {"98"=>"3315", "99"=>"10.*.*.*", "100"=>"255.255.255.240", "101"=>"10.*.*.*", "102"=>"g", "103"=>"", "104"=>"2.0", "105"=>"", "106"=>"", "107"=>"", "108"=>"", "109"=>"111", "110"=>"10.*.*.*", "111"=>"255.255.255.240", "112"=>"10.*.*.*", "113"=>"g", "114"=>"1.0", "115"=>"", "116"=>"", "117"=>"", "135"=>"111", "136"=>"10.*.*.*", "137"=>"255.255.255.240", "138"=>"10.*.*.*", "139"=>"", "140"=>"", "141"=>"", "142"=>"", "143"=>"" }
This is the output:
output = {"Erogazione VlanID"=>"3315", " SubnetorIP"=>"10.*.*.*", " Netmask"=>"255.255.255.240", " Gateway"=>"10.*.*.*", " Speed(f,g)"=>"", " Bond(s/n)"=>"", " Porte"=>"", " Switch"=>"", " Slot/Porte"=>"", " PortePPanel"=>"", " PortePPanel(bond)"=>"", "Backup VlanID"=>"111", "Remote Console VlanID"=>"111"}

Your approach seems to work fine, and will not produce a "missing" entry in the resulting Hash.
a = { a: 1, b: 2, c: nil, d: 4 }
b = { a: 5, b: 6, c: 7, d: 8 }
c = Hash[a.map{|k,v| [b[k],v]}]
# {5=>1, 6=>2, 7=>nil, 8=>4}
No issues for the a[:c] value, which is nil. It will generate the 7 => nil mapping, as b[:c] is 7.

Related

how to update terminal values in realtime

I have the following asc table:
+---------------------------------------------+
| Report |
+----------+----------+-------------+---------+
| Store | Total |
+----------+----------+-------------+---------+
| A | 2723 |
| B | 7277 |
+----------+----------+-------------+---------+
I need to update the total while threre are updates running on my database.
How can I do that?
I already have the method that gets updated total.
But how can I persist the total on the terminal screen?
You can achieve this using the following gems
https://github.com/ruby/curses
https://github.com/tj/terminal-table
Example :
require 'terminal-table'
require "curses"
Curses.init_screen
Curses.crmode
Curses.noecho
Curses.stdscr.keypad = true
begin
x = 0
y = 0
loop do
table = Terminal::Table.new do |t|
t << ['Random 1', Random.rand(1...10)]
t.add_row ['Random 1', Random.rand(10...100)]
end
Curses.setpos(x, y)
output = table.render.to_s
Curses.addstr(output)
Curses.refresh
sleep 1
end
ensure
close_screen
end

Sort hash by values

This is not how I populated my hash. Just for easier reading, here are its contents, keys are on a fixed length string:
my %country_hash = (
"001 Sample Name New Zealand" => "NEW ZEALAND",
"002 Samp2 Nam2 Zimbabwe " => "ZIMBABWE",
"003 SSS NNN Australia " => "AUSTRALIA",
"004 John Sample Philippines" => "PHILIPPINES,
);
I want to get the sorted keys based on values. So my expectation:
"003 SSS NNN Australia "
"001 Sample Name New Zealand"
"004 John Sample Philippines"
"002 Samp2 Nam2 Zimbabwe "
What I did:
foreach my $line( sort {$country_hash{$a} <=> $country_hash{$b} or $a cmp $b} keys %country_hash ){
print "$line\n";
}
also;
(I doubted this will sort but anyway)
my #sorted = sort { $country_hash{$a} <=> $country_hash{$b} } keys %country_hash;
foreach my $line(#sorted){
print "$line\n";
}
Neither of them sorted correctly. I hope someone could help.
If you had used warnings, you would have been told that <=> is the wrong operator; it is used for numeric comparison. Use cmp for string comparison instead. Refer to sort.
use warnings;
use strict;
my %country_hash = (
"001 Sample Name New Zealand" => "NEW ZEALAND",
"002 Samp2 Nam2 Zimbabwe " => "ZIMBABWE",
"003 SSS NNN Australia " => "AUSTRALIA",
"004 John Sample Philippines" => "PHILIPPINES",
);
my #sorted = sort { $country_hash{$a} cmp $country_hash{$b} } keys %country_hash;
foreach my $line(#sorted){
print "$line\n";
}
This prints:
003 SSS NNN Australia
001 Sample Name New Zealand
004 John Sample Philippines
002 Samp2 Nam2 Zimbabwe
This also works (without the extra array):
foreach my $line (sort {$country_hash{$a} cmp $country_hash{$b}} keys %country_hash) {
print "$line\n";
}

Reading a line from a text file, splitting the string version of that line into two parts

Newbie learning Ruby.
I am trying to take a txt file and on each line take the first 3 characters and assign them as a key, and the rest of the string as that's keys value.
f = File.open("textfile.txt", "r")
finalHash = {"Key" => "Data"}
lineString = ""
while f.gets != nil do
lineString = f.gets
part1 = lineString.slice(0, 2)
part2 = lineString.slice(3, lineString.length)
finalHash[:part1] = part2
end
puts finalHash
Any advice is appreciated!
the 2nd parameter of slice is the length, not the end-index, so change:
part1 = lineString.slice(0, 2)
to:
part1 = lineString.slice(0, 3)
If passed a start index and a length, returns a substring containing
length characters starting at the index
Also you don't need the second parameter here (this is not a bug though):
part2 = lineString.slice(3, lineString.length)
This is enough:
part2 = lineString.slice(3)
Let's first create a file:
text = <<_
Now is the
time for all
good Rubiests
to come to the
aid of their
bowling team.
_
FName = 'temp'
File.write(FName, text)
#=> 80
Now read the file a line at a time and construct the desired hash:
File.foreach(FName).with_object({}) do |line, h|
h[line.slice!(0,3)] = line.chomp
end
#=> {"Now"=>" is the", "tim"=>"e for all", "goo"=>"d Rubiests",
# "to "=>"come to the", "aid"=>" of their", "bow"=>"ling team."}
After reading the first line,
h = { "Now"=>" is the" }
line = "time for all\n"
a = line.chomp
#=> "time for all"
b = a.slice!(0,3)
#=> "tim"
a #=> "e for all"
h[b] = a
#=> "e for all"
h #=> {"Now"=>" is the", "tim"=>"e for all"}
No direction is given if a line contains fewer than three characters. That may be something to consider.
lines = File.open("textfile.txt").read.split("\n")
hsh = {}
lines.each do |line|
next if line == ""
hsh[line[0..2]] = line[3..-1]
end
using your method of slowly nibbling at the file
f = File.open("textfile.txt")
hsh = {}
loop do
x = f.gets
break unless x
hsh[x[0..2]] = x[3..-1]
end
Borrowing #Cary's sample file...
text = <<_
Now is the
time for all
good Rubiests
to come to the
aid of their
bowling team.
_
FName = 'temp'
File.write(FName, text)
Now the file exists. Convert it to a 2 dimensional array. This array is trivially converted to a hash
File.foreach(FName).map{|x| [x.slice!(0,3), x]}.to_h
=> {"Now"=>" is the\n", "tim"=>"e for all\n", "goo"=>"d Rubiests\n", "to "=>"come to the\n", "aid"=>" of their\n", "bow"=>"ling team.\n"}
Here you go :
Sample data:
[zatcsv]$ cat foo.txt
TOK UPDATE DATE SHOT TIME AUXHEAT PHASE STATE PGASA PGASZ BGASA BGASZ BGASA2 BGASZ2 PIMPA
PIMPZ PELLET RGEO RMAG AMIN SEPLIM XPLIM KAPPA DELTA INDENT AREA VOL CONFIG IGRADB WALMAT DIVMAT LIMMAT EVAP
BT IP VSURF Q95 BEPMHD BETMHD BEPDIA NEL DNELDT ZEFF PRAD POHM ENBI PINJ BSOURCE PINJ2 BSOURCE2 COCTR PNBI ECHFREQ
ECHMODE ECHLOC PECH ICFREQ ICSCHEME ICANTEN PICRH LHFREQ LHNPAR PLH IBWFREQ PIBW TE0 TI0 WFANI WFICRH MEFF ISEQ WTH WTOT
JET 20031201 20001006 53521 1.000E+01 NBIC HSELM TRANS 2.000E+00 1.000E+00 2 1 0 0 1.658E+01 8.152E+00 NONE 2.888E+00
HEEH OIJ OIJJ 3.047E+00 9.807E-01 2.924E-02 7.304E-02 1.572E+00 1.781E-01 0.000E+00 4.572E+00 8.161E+01 LSN 1 IN/
2.000E+06 1.013E-01 6.001E+00 1.053E+00 9.252E-01 1.128E+00 3.106E+19 3.106E+19 6.612E+00 4.515E+06 5.122E+04 1.000E+05 1.466E+07
771706 0.000E+00 652114 1.000E+00 1.420E+07 -9.999E-09 NONE NONE 0.000E+00 5.100E+07 HMIN MONOPOLE 4.027E+06 3.700E+09 1.840E+00
2.000E+06 -9.999E-09 0.000E+00 9.295E+03 1.373E+04 6.913E-01 7.319E+05 2.000E+00 NONE 3.715E+06 5.381E+06 1.282E+06 1.297E+07 1.210E+07
something like this will do it for you :
[za csv]$cat text_to_hash.rb
#!/usr/bin/env ruby
file_dir = "/dir/to_folder/foo.txt"
thehash = Hash.new
line = File.read(file_dir).each_line do |line|
thehash[ key = line.slice(0..2)] = val = line.slice(3..-1)
thehash.each { |k , val| puts " Key: #{key} Value: #{val}"}
end
Outputs:
[za csv]$ ./text_to_hash.rb
Key: TOK Value: UPDATE DATE SHOT TIME AUXHEAT PHASE STATE PGASA PGASZ BGASA BGASZ BGASA2 BGASZ2 PIMPA
Key: PIM Value: PZ PELLET RGEO RMAG AMIN SEPLIM XPLIM KAPPA DELTA INDENT AREA VOL CONFIG IGRADB WALMAT DIVMAT LIMMAT EVAP
Key: ECH Value: MODE ECHLOC PECH ICFREQ ICSCHEME ICANTEN PICRH LHFREQ LHNPAR PLH IBWFREQ PIBW TE0 TI0 WFANI WFICRH MEFF ISEQ WTH WTOT
Key: JET Value: 20031201 20001006 53521 1.000E+01 NBIC HSELM TRANS 2.000E+00 1.000E+00 2 1 0 0 1.658E+01 8.152E+00 NONE 2.888E+00
Key: HEE Value: H OIJ OIJJ 3.047E+00 9.807E-01 2.924E-02 7.304E-02 1.572E+00 1.781E-01 0.000E+00 4.572E+00 8.161E+01 LSN 1 IN/
Key: 2.0 Value: 00E+06 1.013E-01 6.001E+00 1.053E+00 9.252E-01 1.128E+00 3.106E+19 3.106E+19 6.612E+00 4.515E+06 5.122E+04 1.000E+05 1.466E+07
Key: 771 Value: 706 0.000E+00 652114 1.000E+00 1.420E+07 -9.999E-09 NONE NONE 0.000E+00 5.100E+07 HMIN MONOPOLE 4.027E+06 3.700E+09 1.840E+00
Key: 2.0 Value: 00E+06 -9.999E-09 0.000E+00 9.295E+03 1.373E+04 6.913E-01 7.319E+05 2.000E+00 NONE 3.715E+06 5.381E+06 1.282E+06 1.297E+07 1.210E+07
Key: 4.4 Value: 45E-01 2.194E-01

Number of string value occurrences for distinct another column value

I have a model Counter which returns the following records:
name.....flowers.....counter
vino.....rose.........1
vino.....lily.........1
gaya.....rose.........1
rosi.....lily.........1
vino.....lily.........1
rosi.....rose.........1
rosi.....rose.........1
I want to display in the table like:
name | Rose | Lily |
---------------------
Vino | 1 | 2 |
---------------------
Gaya | 1 | 0 |
---------------------
Rosi | 2 | 1 |
I want to display the count of flowers for each distinct name. I have tried the following and wondering how can I do it elegantly?
def counter_results
#counter_results= {}
Counter.each do |name|
rose = Counter.where(flower: 'rose').count
lily= Counter.where(flower: 'lily').count
#counter_results['name'] = name
#counter_results['rose_count'] = rose
#counter_results['lily_count'] = lily
end
return #counter_results
end
which I don't get the hash values.
This will give you slightly different output, but I think it is probably closer to what you want than what you showed.
You can use the query:
Counter.group([:name, :flowers]).sum(:counter)
To get a result set that looks like:
{ ["vino", "rose"] => 1, ["vino", "lily"] => 2, ["gaya", "rose"] => 1, ["gaya", "lily"] => 0, ... }
And you can do something like this to generate your hash:
def counter_results
#counter_results = {}
Counter.group([:name, :flowers]).sum(:counter).each do |k, v|
#counter_results[k.join("_")] = v
end
#counter_results
end
The resulting hash would look like this:
{
"vino_rose" => 1,
"vino_lily" => 2,
"gaya_rose" => 1,
"gaya_lily" => 0,
...
}
Somebody else may have a better way to do it, but seems like that should get you pretty close.

Ruby CSV re-arranging Array

I'm not sure what the appropriate title for this question so if someone could help me with that also, it would be nice.
-
I have a CSV file that looks something like
ID | Num
a | 1
a | 2
a | 3
b | 4
b | 5
c | 6
c | 7
I need the result to be:
ID | Num
a | 1,2,3,4
b | 4,5
c | 6,7
Currently, my solution is:
ary = CSV.open('some_file')
final = Array.new
id = ary[1][0] # ary[0] is "id"
numJoin = ary[1][1]
(1..ary.length).each do |i|
if id == ary[i+1][0]
numJoin = numJoin + "," + ary[i+1][1]
else
final << [id,numJoin]
id = ary[i+1][0]
numJoin = ary[i+1]]1]
end
end
It works, but I would like to have the opportunity to learn other ways to solve this, as I think there should be simpler ways to do this..
Thanks in advance.
You can use group_by, which groups by the return value of the block passed to it, in this case, it's the ID.
ary = ary.group_by { |v| v[0] }
P.S That file ain't looking like a CSV.

Resources