Combining two arrays into a hash - ruby

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

Related

Modifying an Array

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"}]]

How to check if a key exists in an array of arrays?

Is there a straightforward way to do something like the following without excessive looping?
myArray = [["a","b"],["c","d"],["e","f"]]
if myArray.includes?("c")
...
I know this works fine if it's just a normal array of chars... but I would like something equally as elegant for an array of an array of chars (bonus points for helping convert this to an array of tuples).
If you only need a true/false answer you can flatten the array and call include on that:
>> myArray.flatten.include?("c")
=> true
You can use assoc:
my_array = [['a', 'b'], ['c', 'd'], ['e', 'f']]
if my_array.assoc('c')
# ...
It actually returns the whole subarray:
my_array.assoc('c') #=> ["c", "d"]
or nil if there is no match:
my_array.assoc('g') #=> nil
There's also rassoc to search for the second element:
my_array.rassoc('d') #=> ["c", "d"]
my_array = [["a","b"],["c","d"],["e","f"]]
p my_hash = my_array.to_h # => {"a"=>"b", "c"=>"d", "e"=>"f"}
p my_hash.key?("c") # => true
You can use Array#any?
myArray = [["a","b"],["c","d"],["e","f"]]
if myArray.any? { |x| x.includes?("c") }
# some code here
The find_index method works well for this:
myArray = [["a","b"],["c","d"],["e","f"]]
puts "found!" if myArray.find_index {|a| a[0] == "c" }
The return value is the array index of the pair or nil if the pair is not found.
You can capture the pair's value (or nil if not found) this way:
myArray.find_index {|a| a[0] == "c" } || [nil, nil])[1]
# => "d"

Set multiple keys to the same value at once for a Ruby hash

I'm trying to create this huge hash, where there are many keys but only a few values.
So far I have it like so...
du_factor = {
"A" => 1,
"B" => 1,
"C" => 1,
"D" => 2,
"E" => 2,
"F" => 2,
...etc., etc., etc., on and on and on for longer than you even want to know. What's a shorter and more elegant way of creating this hash without flipping its structure entirely?
Edit: Hey so, I realized there was a waaaay easier and more elegant way to do this than the answers given. Just declare an empty hash, then declare some arrays with the keys you want, then use a for statement to insert them into the array, like so:
du1 = ["A", "B", "C"]
du2 = ["D", "E", "F"]
dufactor = {}
for i in du1
dufactor[i] = 1
end
for i in du740
dufactor[i] = 2
end
...but the fact that nobody suggested that makes me, the extreme Ruby n00b, think that there must be a reason why I shouldn't do it this way. Performance issues?
Combining Ranges with a case block might be another option (depending on the problem you are trying to solve):
case foo
when ('A'..'C') then 1
when ('D'..'E') then 2
# ...
end
Especially if you focus on your source code's readability.
How about:
vals_to_keys = {
1 => [*'A'..'C'],
2 => [*'D'..'F'],
3 => [*'G'..'L'],
4 => ['dog', 'cat', 'pig'],
5 => [1,2,3,4]
}
vals_to_keys.each_with_object({}) { |(v,arr),h| arr.each { |k| h[k] = v } }
#=> {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2, "F"=>2, "G"=>3, "H"=>3, "I"=>3,
# "J"=>3, "K"=>3, "L"=>3, "dog"=>4, "cat"=>4, "pig"=>4, 1=>5, 2=>5, 3=>5, 4=>5}
What about something like this:
du_factor = Hash.new
["A", "B", "C"].each {|ltr| du_factor[ltr] = 1}
["D", "E", "F"].each {|ltr| du_factor[ltr] = 2}
# Result:
du_factor # => {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2, "F"=>2}
Create an empty hash, then for each group of keys that share a value, create an array literal containing the keys, and use the array's '.each' method to batch enter them into the hash. Basically the same thing you did above with for loops, but it gets it done in three lines.
keys = %w(A B C D E F)
values = [1, 1, 1, 2, 2, 2]
du_factor = Hash[*[keys, values].transpose.flatten]
If these will be more than 100, writing them down to a CSV file might be better.
keys = [%w(A B C), %w(D E F)]
values = [1,2]
values.map!.with_index{ |value, idx| Array(value) * keys[idx].size }.flatten!
keys.flatten!
du_factor = Hash[keys.zip(values)]
Notice here that I used destructive methods (methods ending with !). this is important for performance and memory usage optimization.

Ruby array to hash: each element the key and derive value from it

I have an array of strings, and want to make a hash out of it. Each element of the array will be the key, and I want to make the value being computed from that key. Is there a Ruby way of doing this?
For example:
['a','b'] to convert to {'a'=>'A','b'=>'B'}
You can:
a = ['a', 'b']
Hash[a.map {|v| [v,v.upcase]}]
%w{a b c}.reduce({}){|a,v| a[v] = v.upcase; a}
Ruby's each_with_object method is a neat way of doing what you want
['a', 'b'].each_with_object({}) { |k, h| h[k] = k.upcase }
Here's another way:
a.zip(a.map(&:upcase)).to_h
#=>{"a"=>"A", "b"=>"B"}
Which ever way you look at it you will need to iterate the initial array. Here's another way :
a = ['a', 'b', 'c']
h = Hash[a.collect {|v| [v, v.upcase]}]
#=> {"a"=>"A", "b"=>"B", "c"=>"C"}
Here's a naive and simple solution that converts the current character to a symbol to be used as the key. And just for fun it capitalizes the value. :)
h = Hash.new
['a', 'b'].each {|a| h[a.to_sym] = a.upcase}
puts h
# => {:a=>"A", :b=>"B"}
From Rails 6.x, you can use Enumerable#index_with:
irb(main):002:0> ['a', 'b'].index_with {|s| s.upcase}
=> {"a"=>"A", "b"=>"B"}
Pass a block to .to_h
[ 'a', 'b' ].to_h{ |element| [ element, element.upcase ] }
#=> {"a"=>"A", "b"=>"B"}
Thanks to #SMAG for the refactor suggestion!
Not sure if this is the real Ruby way but should be close enough:
hash = {}
['a', 'b'].each do |x|
hash[x] = x.upcase
end
p hash # prints {"a"=>"A", "b"=>"B"}
As a function we would have this:
def theFunk(array)
hash = {}
array.each do |x|
hash[x] = x.upcase
end
hash
end
p theFunk ['a', 'b', 'c'] # prints {"a"=>"A", "b"=>"B", "c"=>"C"}

simple hash merge by array of keys and values in ruby (with perl example)

In Perl to perform a hash update based on arrays of keys and values I can do something like:
#hash{'key1','key2','key3'} = ('val1','val2','val3');
In Ruby I could do something similar in a more complicated way:
hash.merge!(Hash[ *[['key1','key2','key3'],['val1','val2','val3']].transpose ])
OK but I doubt the effectivity of such procedure.
Now I would like to do a more complex assignment in a single line.
Perl example:
(#hash{'key1','key2','key3'}, $key4) = &some_function();
I have no idea if such a thing is possible in some simple Ruby way. Any hints?
For the Perl impaired, #hash{'key1','key2','key3'} = ('a', 'b', 'c') is a hash slice and is a shorthand for something like this:
$hash{'key1'} = 'a';
$hash{'key2'} = 'b';
$hash{'key3'} = 'c';
In Ruby 1.9 Hash.[] can take as its argument an array of two-valued arrays (in addition to the old behavior of a flat list of alternative key/value arguments). So it's relatively simple to do:
mash.merge!( Hash[ keys.zip(values) ] )
I do not know perl, so I'm not sure what your final "more complex assignment" is trying to do. Can you explain in words—or with the sample input and output—what you are trying to achieve?
Edit: based on the discussion in #fl00r's answer, you can do this:
def f(n)
# return n arguments
(1..n).to_a
end
h = {}
keys = [:a,:b,:c]
*vals, last = f(4)
h.merge!( Hash[ keys.zip(vals) ] )
p vals, last, h
#=> [1, 2, 3]
#=> 4
#=> {:a=>1, :b=>2, :c=>3}
The code *a, b = some_array will assign the last element to b and create a as an array of the other values. This syntax requires Ruby 1.9. If you require 1.8 compatibility, you can do:
vals = f(4)
last = vals.pop
h.merge!( Hash[ *keys.zip(vals).flatten ] )
You could redefine []= to support this:
class Hash
def []=(*args)
*keys, vals = args # if this doesn't work in your version of ruby, use "keys, vals = args[0...-1], args.last"
merge! Hash[keys.zip(vals.respond_to?(:each) ? vals : [vals])]
end
end
Now use
myhash[:key1, :key2, :key3] = :val1, :val2, :val3
# or
myhash[:key1, :key2, :key3] = some_method_returning_three_values
# or even
*myhash[:key1, :key2, :key3], local_var = some_method_returning_four_values
you can do this
def some_method
# some code that return this:
[{:key1 => 1, :key2 => 2, :key3 => 3}, 145]
end
hash, key = some_method
puts hash
#=> {:key1 => 1, :key2 => 2, :key3 => 3}
puts key
#=> 145
UPD
In Ruby you can do "parallel assignment", but you can't use hashes like you do in Perl (hash{:a, :b, :c)). But you can try this:
hash[:key1], hash[:key2], hash[:key3], key4 = some_method
where some_method returns an Array with 4 elements.

Resources