Modifying an Array - ruby

I have an Array like this
example_array = ['dog', 'cat', 'snake']
And I am trying to append the timestamp to each element of the array and the output should look like
example_array = [{'dog': 'time_stamp'},{'cat':'time_stamp'},{'snake':'time_stamp'}]
I've tried this but the output is incorrect:
a = {}
example_array.each_with_index do |element, i|
a.merge!("#{element}": "#{Time.now}")
example_array.delete_at(i)
end
Can anyone suggest me a solution in ruby?
I have tried a lot of ways but couldn't obtain the output like above.

Aditha,
How about this?
array = ["cat", "hat", "bat", "mat"]
hash = []
hash.push(Hash[array.collect { |item| [item, Time.now] } ])
OUTPUT: => [{"cat"=>"2018-02-28 04:23:08 UTC", "hat"=>"2018-02-28 04:23:08 UTC", "bat"=>"2018-02-28 04:23:08 UTC", "mat"=>"2018-02-28 04:23:08 UTC"}]
Instead of item.upcase you would insert your timestamp info. It gives me hashes inside of array.

example_array.product([Time.now]).map { |k,v| { k.to_sym=>v }}
#=> [{:dog=>2018-02-27 20:42:56 -0800},
# {:cat=>2018-02-27 20:42:56 -0800},
# {:snake=>2018-02-27 20:42:56 -0800}
]Note this ensures that all values (timestamps) are equal.

only weird thing is that you have to use => instead of :
arr = ['dog', 'cat', 'snake']
arr2 = []
for index in 0 ... arr.size
arr2.push({arr[index] => Time.now})
end
puts arr2

['dog', 'cat', 'snake'].map{|e| [{e.to_sym => "time_stamp"}]}
# => [[{:dog=>"time_stamp"}], [{:cat=>"time_stamp"}], [{:snake=>"time_stamp"}]]

Related

Create a new hash from an existing array of hashes

I am newbie to ruby . I have an array of hashes input_array
[{
"name"=>"test1",
"zone_status"=>"valid",
"certificate_status"=>"valid",
"users"=>1000,
"name"=>"test2",
"zone_status"=>"valid",
"certificate_status"=>"valid",
"users"=>5000,
"name"=>"test3",
"zone_status"=>"valid",
"certificate_status"=>"valid",
"users"=>3000,
"name"=>"test4",
"zone_status"=>"valid",
"certificate_status"=>"valid",
"users"=>2000}]
and an array
existing_names_array = ["test1","test2"]
The below line gets me all the names into input_names
input_array.each_with_index {|val, index| input_names << input_array[index]['name'] }
But how can I get the input_names to be a hash with name as key and its respective users as value?
Because my final goal is to check the names which are in input_names, but not in existing_names_array and get a count of those names and users
As the names tes1,test2 exists in existing_names_array, I need the count of rest of names and count of their respective users in input_array
Expected output:
output_names = test3, test 4
total_output_names = 2
output_users = 3000,2000
total_output_users = 5000
If you use ActiveSupport's Enumerable#index_by with Ruby core's Hash#transform_values it's pretty easy, if I'm understanding your question correctly:
# add gem 'activesupport' to Gemfile, or use Rails, then ..
require 'active_support/all'
users_and_counts = input_array.
index_by { |hsh| hsh["name"] }.
transform_values { |hsh| hsh["users"] }
# => { "test1" => 1000, "test2" => 5000, ... }
You can do this with Enumerable#reduce (or Enumerable#each_with_object) as well:
users_and_counts = input_array.reduce({}) do |memo, hsh|
memo[hsh["name"]] = hsh["users"]
memo
end
Or, the simplest way, with good old each:
users_and_counts = {}
input_array.each do |hsh|
users_and_counts[hsh["name"]] = hsh["users"]
end
in response to comment
In general, there are a few ways to check whether an element is found in an array:
array = ["foo", "bar"]
# 1. The simple standard way
array.include?("foo") # => true
# 2. More efficient way
require 'set'
set = Set.new(array)
set.member?("foo") # => true
So with this knowledge we can break up our task into a few steps:
Make a new hash which is a copy of the one we built above, but without the key-vals corresponding to users in the existing_names_array (see Hash#select and Hash#reject):
require 'set'
existing_names_set = Set.new(existing_names_array)
new_users_and_counts = users_and_counts.reject do |name, count|
existing_names_set.member?(name)
end
# => { "test3" => 3000, "test4" => 2000 }
Use Hash#keys to get the list of user names:
new_user_names = new_users_and_counts.keys
# => ["test3", "test4"]
Use Hash#values to get the list of counts:
new_user_counts = new_users_and_counts.values
# => [3000, 2000]
I assume input_array is to be as follows.
input_array = [
{ "name"=>"test1", "zone_status"=>"valid", "certificate_status"=>"valid",
"users"=>1000 },
{ "name"=>"test2", "zone_status"=>"valid", "certificate_status"=>"valid",
"users"=>5000 },
{ "name"=>"test3", "zone_status"=>"valid", "certificate_status"=>"valid",
"users"=>3000 },
{ "name"=>"test4", "zone_status"=>"valid", "certificate_status"=>"valid",
"users"=>2000}
]
We are also given:
names = ["test1", "test2"]
The information of interest can be represented nicely by the following hash (which I will construct):
summary = { "test1"=>1000, "test2"=>5000 }
We might use that to compute the following.
names = summary.keys
#=> ["test1", "test2"]
users = summary.values
#=> [1000, 5000]
total_users = users.sum
#=> 6000
There are many ways to construct the hash summary. I prefer the following.
summary = input_array.each_with_object({}) do |g,h|
key = g["name"]
h[key] = g["users"] if names.include?(key)
end
#=> {"test1"=>1000, "test2"=>5000}
Another way is as follows.
summary = input_array.map { |g| g.values_at("name", "users") }.
.to_h
.slice(*names)
See [Hash#values_at(https://ruby-doc.org/core-2.7.0/Hash.html#method-i-values_at), Array#to_h and Hash#slice. The steps are as follows.
arr = input_array.map { |g| g.values_at("name", "users") }
#=> [["test1", 1000], ["test2", 5000], ["test3", 3000], ["test4", 2000]]
h = arr.to_h
#=> {"test1"=>1000, "test2"=>5000, "test3"=>3000, "test4"=>2000}
summary = h.slice(*names)
#=> {"test1"=>1000, "test2"=>5000}
The splat operator converts h.slice(*names), which is h.slice(*["test1", "test2"]), to h.slice("test1", "test2"). That's because Hash#slice was defined to accept a variable number of keys as arguments, rather than an array of keys as the argument.

Initializing a hash with an empty array keyed to an array of strings - Ruby

I have:
people=["Bob","Fred","Sam"]
holidays = Hash.new
people.each do |person|
a=Array.new
holidays[person]=a
end
gifts = Hash.new
people.each do |person|
a=Array.new
gifts[person]=a
end
Feels clunky. I can't seem to figure a more streamline way with an initialization block or somesuch thing. Is there an idiomatic approach here?
Ideally, I'd like to keep an array like:
lists["holidays","gifts",...]
... and itterate through it to initialize each element in the lists array.
people = %w|Bob Fred Sam|
data = %w|holidays gifts|
result = data.zip(data.map { people.zip(people.map { [] }).to_h }).to_h
result['holidays']['Bob'] << Date.today
#⇒ {
# "holidays" => {
# "Bob" => [
# [0] #<Date: 2016-11-04 ((2457697j,0s,0n),+0s,2299161j)>
# ],
# "Fred" => [],
# "Sam" => []
# },
# "gifts" => {
# "Bob" => [],
# "Fred" => [],
# "Sam" => []
# }
# }
More sophisticated example would be:
result = data.map do |d|
[d, Hash.new { |h, k| h[k] = [] if people.include?(k) }]
end.to_h
The latter produces the “lazy initialized nested hashes.” It uses the Hash#new with a block constructor for nested hashes.
Play with it to see how it works.
A common way of doing that would be to use Enumerable#each_with_objrect.
holidays = people.each_with_object({}) { |p,h| h[p] = [] }
#=> {"Bob"=>[], "Fred"=>[], "Sam"=>[]}
gifts is the same.
If you only want a number of such hashes then, the following should suffice:
count_of_hashes = 4 // lists.count; 4 is chosen randomly by throwing a fair die
people = ["Bob", "Fred", "Sam"]
lists = count_of_hashes.times.map do
people.map {|person| [person, []]}.to_h
end
This code also ensures the arrays and the hashes all occupy their own memory. As can be verified by the following code:
holidays, gifts, *rest = lists
holidays["Bob"] << "Rome"
And checking the values of all the other hashes:
lists
=> [
{"Bob"=>["Rome"], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]}
]

ruby set key of hash for array of hashes

Given the following array of hashes, how can I make a new hash with friend_id as the key and dist the value?
results = [
{"user_id"=>"18", "friend_id"=>"17", "dist"=>"1"},
{"user_id"=>"18", "friend_id"=>"42", "dist"=>"1"},
{"user_id"=>"18", "friend_id"=>"43", "dist"=>"1"},
{"user_id"=>"18", "friend_id"=>"46", "dist"=>"2"}
]
desired_hash = {"17" => "1", "42" => "1", "43" => "1", "46" => "2"}
I've tried map but the values are then in an array. I also tried to flatten that result but it flattened the key instead of the value
results.each_with_object({}) { |g,h| h[g["friend_id"]] = g["dist"] }
or
results.each_with_object({}) { |g,h| h.update(g["friend_id"]=> g["dist"]) }
For this use case, I think it would be simpler and more readable to just use #each:
desired_hash = Hash.new
results.each {|h| desired_hash[h["friend_id"]] = h["dist"]}
Then, desired_hash is:
#=> {"17"=>"1", "42"=>"1", "43"=>"1", "46"=>"2"}
You can use Enumerable#inject method.
results.inject({}) {|sum, e| sum.merge({e["friend_id"] => e["dist"]})}
# => {"17"=>"1", "42"=>"1", "43"=>"1", "46"=>"2"}
results.map {|h| [h['friend_id'], h['dist']]} .to_h
Although I probably like #CarySwoveland 's answer better, on the lines of:
results.each_with_object({}) {|h, n| n[h['friend_id']] = h['dist']}
Simply:
desired_hash = Hash[results.map{ |h| [ h['friend_id'], h['dist']] }]
or as Victor suggests
desired_hash = Hash[results.map{ |x| x.values_at('friend_id', 'dist') }]

Combining two arrays into a hash

I'm trying to combine two arrays into a hash.
#sample_array = ["one", "Two", "Three"]
#timesheet_id_array = ["96", "97", "98"]
I want to output the results into a hash called #hash_array. Is there a simple way to combine the two in a code block so that if you call puts at the end it looks like this in the console
{"one" => "96", "Two" => "97", "Three" => "98"}
I think this could be done in one or two lines of code.
try this
keys = [1, 2, 3]
values = ['a', 'b', 'c']
Hash[keys.zip(values)]
thanks
#hash_array = {}
#sample_array.each_with_index do |value, index|
#hash_array[value] = #timesheet_id_array[index]
end
Imho that looks best:
[:a,:b,:c].zip([1,2,3]).to_h
# {:a=>1, :b=>2, :c=>3}
Dr. Nic suggests 2 options explained well at http://drnicwilliams.com/2006/10/03/zip-vs-transpose/
#hash_array = {}
0.upto(#sample_array.length - 1) do |index|
#hash_array[#sample_array[index]] = #timesheet_id_array[index]
end
puts #hash_array.inspect

Populate ruby Array1 with Array2 String element if and only if Array2 element matches Hash value(not key)

I have a ruby hash:
VALS = { :one => "One", :two => "Two" }
and an Array:
array2 = ["hello", "world", "One"]
Question: How can I populate a new array1 so that it only pulls in any values in array2 that match exactly the values in VALS?
For example, I have tried:
array2.each_with_index do |e,i|
array1 << e if VALS[i] ~= e
end
Along with other thing, and none work. Noob.
Thanks
brilliant! but whent I tried:
p array.select { |i| hash.has_value? i ? array[i+1] : "foo"}
I got an can't convert fixnum error. I must be missing something.
Using nested loops would be very slow if both collections are large. It's better to treat the contents as sets:
array1 = VALS.values & array2
print array1
Output:
One
Here's an option:
hash = { :one => "One", :two => "Two" }
array = ["hello", "world", "One"]
p array.select { |i| hash.has_value? i }
# >> ["One"]
got it!
array.select do |i|
if VALS.has_value? i
result << array[array.index(i)+1]
end
end

Resources