how do i loop an array in ruby? - ruby

Hi i just started learning ruby and i'm trying to loop through an array i created looking like this:
server[0] = ['hostname' => 'unknown01', 'ip' => '192.168.0.2', 'port' => '22']
server[1] = ['hostname' => 'unknown02', 'ip' => '192.168.0.3', 'port' => '23']
server[2] = ['hostname' => 'unknown03', 'ip' => '192.168.0.4', 'port' => '24']
i tried using this code:
i=0
server[i].each do |x|
print x['hostname']
print x['ip']
i+=1
end
but it only loops through server[0] how i can loop through server[0-3]

You don't need to use i at all, just do this:
server.each do |x|
print x['hostname']
print x['ip']
end

What you're doing is setting i to 0, so when you get to the server[i].each, you're calling server[0].each. Even though you increment i that doesn't change what you're enumerating over. The correct code is:
server.each
to enumerate over every element in server.

This probably isn't doing what you think it is. Since you're putting your key/value pairs inside of square brackets, it winds up being an array of one hash (other languages call them maps and dictionaries). Probably you want to exchange your square brackets with curly brackets like this:
servers = []
servers[0] = {'hostname' => 'unknown01', 'ip' => '192.168.0.2', 'port' => '22'}
servers[1] = {'hostname' => 'unknown02', 'ip' => '192.168.0.3', 'port' => '23'}
servers[2] = {'hostname' => 'unknown03', 'ip' => '192.168.0.4', 'port' => '24'}
But since you're just setting each one by its index, you don't need to build it up this way, you can just place them in their respective positions within the array.
servers = [
{'hostname' => 'unknown01', 'ip' => '192.168.0.2', 'port' => '22'},
{'hostname' => 'unknown02', 'ip' => '192.168.0.3', 'port' => '23'},
{'hostname' => 'unknown03', 'ip' => '192.168.0.4', 'port' => '24'},
]
Then to iterate over each one you can do:
servers.each do |server|
puts server['hostname']
puts server['ip']
puts
end

Related

How to sum array values and add total at the end in the same array

Good Afternoon,
Hello, i am new to coding and Laravel and working on home project as a self leaner. i stuck at array sum where i want to make the sum of values in array and add the same sum at the end of tjat array itself.
array:7 [▼
"2022-12-04" => array:9 [▼
"startdate" => "2022-12-04"
"Stotalbmilk" => "29.00"
"Stotala2milk" => "22.50"
"Stotaljmilk" => "20.00"
"Stotalmilk" => "71.50"
"Dtotalbmilk" => "40.00"
"Dtotala2milk" => "0.00"
"Dtotaljmilk" => "0.00"
"Dtotalmilk" => "40.00"
]
in the above array i am to add "TOTAL" at he bottom which value will be addition of 2 values ( "Stotalmilk" and "Dtotalmilk" ). Expected array will be
array:7 [▼
"2022-12-04" => array:9 [▼
"startdate" => "2022-12-04"
"Stotalbmilk" => "29.00"
"Stotala2milk" => "22.50"
"Stotaljmilk" => "20.00"
**"Stotalmilk" => "71.50"**
"Dtotalbmilk" => "40.00"
"Dtotala2milk" => "0.00"
"Dtotaljmilk" => "0.00"
**"Dtotalmilk" => "40.00"**
**"TOTAL" => "111.50"**
]
hope i properly explain my question and sorry for poor english.
Thanks in advance
So if your array have many dates with data, you can do something like:
$result = [];
foreach ($array as $date => $amounts) {
$total = (float) $amounts['Stotalmilk'] + (float) $amounts['Dtotalmilk'];
$amounts['TOTAL'] = number_format($total, 2, '.');
$result[$date] = $amounts;
}

Elegantly return value(s) matching criteria from an array of nested hashes - in one line

I have been searching for a solution to this issue for a couple of days now, and I'm hoping someone can help out. Given this data structure:
'foo' => {
'bar' => [
{
'baz' => {'faz' => '1.2.3'},
'name' => 'name1'
},
{
'baz' => {'faz' => '4.5.6'},
'name' => 'name2'
},
{
'baz' => {'faz' => '7.8.9'},
'name' => 'name3'
}
]
}
I need to find the value of 'faz' that begins with a '4.', without using each. I have to use the '4.' value as a key for a hash I will create while looping over 'bar' (which obviously I can't do if I don't yet know the value of '4.'), and I don't want to loop twice.
Ideally, there would be an elegant one-line solution to return the value '4.5.6' to me.
I found this article, but it doesn't address the full complexity of this data structure, and the only answer given for it is too verbose; the looping-twice solution is more readable. I'm using Ruby 2.3 on Rails 4 and don't have the ability to upgrade. Are there any Ruby gurus out there who can guide me?
You can use select to filter results.
data = {'foo' => {'bar' => [{'baz' => {'faz' => '1.2.3'}, 'name' => 'name1'}, {'baz' => {'faz' => '4.5.6'}, 'name' => 'name2'}, {'baz' => {'faz' => '7.8.9'}, 'name' => 'name3'}]}}
data.dig('foo', 'bar').select { |obj| obj.dig('baz', 'faz').slice(0) == '4' }
#=> [{"baz"=>{"faz"=>"4.5.6"}, "name"=>"name2"}]
# or if you prefer the square bracket style
data['foo']['bar'].select { |obj| obj['baz']['faz'][0] == '4' }
The answer assumes that every element inside the bar array has the nested attributes baz -> faz.
If you only expect one result you can use find instead.

Performance - Ruby - Compare large array of hashes (dictionary) to primary hash; update resulting value

I'm attempting to compare my data, which is in the format of an array of hashes, with another large array of hashes (~50K server names and tags) which serves as a dictionary. The dictionary is stripped down to only include the absolutely relevant information.
The code I have works but it is quite slow on this scale and I haven't been able to pinpoint why. I've done verbose printing to isolate the issue to a specific statement (tagged via comments below)--when it is commented out, the code runs ~30x faster.
After reviewing the code extensively, I feel like I'm doing something wrong and perhaps Array#select is not the appropriate method for this task. Thank you so much in advance for your help.
Code:
inventory = File.read('inventory_with_50k_names_and_associate_tag.csv')
# Since my CSV is headerless, I'm forcing manual headers
#dictionary_data = CSV.parse(inventory).map do |name|
Hash[ [:name, :tag].zip(name) ]
end
# ...
# API calls to my app to return an array of hashes is not shown (returns '#app_data')
# ...
#app_data.each do |issue|
# Extract base server name from FQDN (e.g. server_name1.sub.uk => server_name1)
derived_name = issue['name'].split('.').first
# THIS IS THE BLOCK OF CODE that slows down execution 30 fold:
#dictionary_data.select do |src_server|
issue['tag'] = src_server[:tag] if src_server[:asset_name].start_with?(derived_name)
end
end
Sample Data Returned from REST API (#app_data):
#app_data = [{'name' => 'server_name1.sub.emea', 'tag' => 'Europe', 'state' => 'Online'}
{'name' => 'server_name2.sub.us', 'tag' => 'US E.', 'state' => 'Online'}
{'name' => 'server_name3.sub.us', 'tag' => 'US W.', 'state' => 'Failover'}]
Sample Dictionary Hash Content:
#dictionary_data = [{:asset_name => 'server_name1-X98765432', :tag => 'Paris, France'}
{:asset_name => 'server_name2-Y45678920', :tag => 'New York, USA'}
{:asset_name => 'server_name3-Z34534224', :tag => 'Portland, USA'}]
Desired Output:
#app_data = [{'name' => 'server_name1', 'tag' => 'Paris, France', 'state' => 'Up'}
{'name' => 'server_name2', 'tag' => 'New York, USA', 'state' => 'Up'}
{'name' => 'server_name3', 'tag' => 'Portland, USA', 'state' => 'F.O'}]
Assuming "no" on both of my questions in the comments:
#!/usr/bin/env ruby
require 'csv'
#dictionary_data = CSV.open('dict_data.csv') { |csv|
Hash[csv.map { |name, tag| [name[/^.+(?=-\w+$)/], tag] }]
}
#app_data = [{'name' => 'server_name1.sub.emea', 'tag' => 'Europe', 'state' => 'Online'},
{'name' => 'server_name2.sub.us', 'tag' => 'US E.', 'state' => 'Online'},
{'name' => 'server_name3.sub.us', 'tag' => 'US W.', 'state' => 'Failover'}]
STATE_MAP = {
'Online' => 'Up',
'Failover' => 'F.O.'
}
#app_data = #app_data.map do |server|
name = server['name'][/^[^.]+/]
{
'name' => name,
'tag' => #dictionary_data[name],
'state' => STATE_MAP[server['state']],
}
end
p #app_data
# => [{"name"=>"server_name1", "tag"=>"Paris, France", "state"=>"Up"},
# {"name"=>"server_name2", "tag"=>"New York, USA", "state"=>"Up"},
# {"name"=>"server_name3", "tag"=>"Portland, USA", "state"=>"F.O."}]
EDIT: I find it more convenient here to read the CSV without headers, as I don't want it to generate an array of hashes. But to read a headerless CSV as if it had headers, you don't need to touch the data itself, as Ruby's CSV is quite powerful:
CSV.read('dict_data.csv', headers: %i(name tag)).map(&:to_hash)

Set up PhpStorm+Vagrant+Xdebug

I've already banged my head to the wall trying to set up debug process on Vagrant virtual machine.
I've got Windows 8.1 on my host machine and Ubuntu 14 onthe guest machine.
Here is my xdebug.ini:
zend_extension="/usr/lib/php5/20121212/xdebug.so"
xdebug.remote_log=/var/xdebug.log
xdebug.remote_host="10.0.2.2"
xdebug.remote_port="8000"
xdebug.remote_enable=1
xdebug.remote_autostart=0
xdebug.remote_handler="dbgp"
xdebug.idekey=vagrant
But in a log file I see such message:
Log opened at 2014-11-13 04:32:18
I: Checking remote connect back address.
I: Remote address found, connecting to 10.10.10.1:9000.
E: Time-out connecting to client. :-(
Log closed at 2014-11-13 04:32:18
Why there are different ip and port?
I've searched around where it can be overwritten, but no luck. In php.ini I found nothing.
Also strange thing: in my path mapping settings at PHPStorm I have slashes replaced by backslahes. I don't know, is it come to a problem.
EDIT: Here is result of php -i | grep xdebug
24:/etc/php5/cli/conf.d/20-xdebug.ini,
47: with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
767:xdebug
769:xdebug support => enabled
777:xdebug.auto_trace => Off => Off
778:xdebug.cli_color => 0 => 0
779:xdebug.collect_assignments => Off => Off
780:xdebug.collect_includes => On => On
781:xdebug.collect_params => 0 => 0
782:xdebug.collect_return => Off => Off
783:xdebug.collect_vars => Off => Off
784:xdebug.coverage_enable => On => On
785:xdebug.default_enable => On => On
786:xdebug.dump.COOKIE => no value => no value
787:xdebug.dump.ENV => no value => no value
788:xdebug.dump.FILES => no value => no value
789:xdebug.dump.GET => no value => no value
790:xdebug.dump.POST => no value => no value
791:xdebug.dump.REQUEST => no value => no value
792:xdebug.dump.SERVER => no value => no value
793:xdebug.dump.SESSION => no value => no value
794:xdebug.dump_globals => On => On
795:xdebug.dump_once => On => On
796:xdebug.dump_undefined => Off => Off
797:xdebug.extended_info => On => On
798:xdebug.file_link_format => no value => no value
799:xdebug.idekey => vagrant => vagrant
800:xdebug.max_nesting_level => 100 => 100
801:xdebug.overload_var_dump => On => On
802:xdebug.profiler_aggregate => Off => Off
803:xdebug.profiler_append => Off => Off
804:xdebug.profiler_enable => Off => Off
805:xdebug.profiler_enable_trigger => Off => Off
806:xdebug.profiler_output_dir => /tmp => /tmp
807:xdebug.profiler_output_name => cachegrind.out.%p => cachegrind.out.%p
808:xdebug.remote_autostart => Off => Off
809:xdebug.remote_connect_back => On => On
810:xdebug.remote_cookie_expire_time => 3600 => 3600
811:xdebug.remote_enable => On => On
812:xdebug.remote_handler => dbgp => dbgp
813:xdebug.remote_host => 10.0.2.2 => 10.0.2.2
814:xdebug.remote_log => /var/xdebug.log => /var/xdebug.log
815:xdebug.remote_mode => req => req
816:xdebug.remote_port => 10000 => 10000
817:xdebug.scream => Off => Off
818:xdebug.show_exception_trace => Off => Off
819:xdebug.show_local_vars => Off => Off
820:xdebug.show_mem_delta => Off => Off
821:xdebug.trace_enable_trigger => Off => Off
822:xdebug.trace_format => 0 => 0
823:xdebug.trace_options => 0 => 0
824:xdebug.trace_output_dir => /tmp => /tmp
825:xdebug.trace_output_name => trace.%c => trace.%c
826:xdebug.var_display_max_children => 128 => 128
827:xdebug.var_display_max_data => 512 => 512
828:xdebug.var_display_max_depth => 3 => 3
Alright, so my problem has been resolved by setting remote_connect_back option to Off. Important note: I have used PuPHPet to generate Vagrant config file, and I didn't know, that there, in the PuPHPet config file, xDebug configuration dwells. Hence somehow host and port was always 10.10.10.10 and 9000 respectively. So it seems that all configurations have to be done by the initial Vagrant config, not by the SSH afterwards.

How to do mongoid 'not_in' 'greater than' query

If I want to search a mongoid model with attribute greater than 100 I would do this.
Model.where({'price' => {'$gt' => 100}})
How do I do search a mongoid model without attribute greater than 100?
Tried this and failed.
Model.not_in({'price' => [{'$gt' => 100}]})
Additional info:
In the end of the day would like to make a query like so:
criteria = {
'price' => [{'$gt' => 100}],
'size' => 'large',
'brand' => 'xyz'
}
Model.not_in(criteria)
As the criteria would be dynamically created.
model without attribute greater than 100 = model with attribute less than or equal to 100?
Model.where({'price' => {'$lte' => 100}})
Try this
Model.where(:price.lte => 100,:size.ne => 'large',:brand.ne => 'xzy')
Try using the .ne() (not equals) operator
Model.where({:price.lte => 100}).ne({:size => 'large', :brand => 'xzy'})
You can also find the Mongoid documentation here http://mongoid.org/en/origin/docs/selection.html#negation

Resources