How to build a new Ruby array from a Hash keys - arrays

I want to build a new array starting from a hash with the following format:
HashConst = {[120,240] => 60, [240,480]=> 30} #Constant
I need to build a new array and assign as value to a new constant with the following format:
[ [[120,240] ,1], [[240,480], 1] ]
I tried :
NewArrayConst = HashConst.keys.each{ |res| [res, 1]}
but I get instead
[ [120,240], [240,480] ]
Only solution I found is the following:
tempVar = []
HashConst.keys.each_with_index{ |res,idx| tempVar [idx] = [res, 1]}
NewArrayConst = tempVar
Anyone knows a better solution to this and can explain why I cannot get the output I expect from NewArrayConst = HashConst.keys.each{ |res| [res, 1]}. I'm using 2.2.2-p95
Edit:
As many pointed out Hash var name is wrong and misleading, I have updated that to avoid confusion

You need to use map instead of each.
Array#each method does not return the result of the code executed in the block, but instead just returns the array on which each was called, which in your case is the value of hash.keys.
Array#map collects the values returned by block into an array.
hash = {[120,240] => 60, [240,480]=> 30}
p array = hash.keys.map{ |res| [res, 1]}
#=> [[[120, 240], 1], [[240, 480], 1]]
NOTE: Do not name your variable Hash as it is already a well-known class in Ruby. Use lower case hash if need be. Also, avoid camel case for variable names such as NewArrayConst, as Ruby recommends use of snake_case for naming variables - you can refer Ruby Style Guide for more details.

h = {[120,240] => 60, [240,480]=> 30}
val = 1
h.keys.product([val])
#=> [[[120, 240], 1], [[240, 480], 1]]

Have you tried Hash.to_a? Sometimes things are easier than you think.

Related

In Ruby, during iteration of an array, how do I send multiple array elements as values for one specific hash key?

I know how to iterate through an array and I know how to create hash keys and values. I do not know how to create an array for the value and pass it multiple elements. My desired value for hash below is:
{'abc' => [1, 2, 3] , 'def' => [4,5,6,7]}
How would I achieve this hash, while iterating through array a and b below using each?
a = [1,2,3,4,5,6,7]
c = [1,2,3]
b = ['abc', 'def']
hash = {}
From your guidelines given in the comment:
While iterating through array a, if the element of iteration is included in array c, it is passed to the array value within key 'abc'. Otherwise, it is passed to other array value in key 'def'
You can do this:
hash = {}
hash['abc'] = a.select { |x| c.include?(x) }
hash['def'] = a.reject{ |x| c.include?(x) }
See Enumerable#select and Enumerable#reject. Also can take a look at Enumerable#partition which would be another good choice here, where you want to split an array into two arrays based on some condition:
in_a, not_in_a = a.partition { |x| c.include?(x) }
hash = { 'abc' => in_a, 'def' => not_in_a }
You can also do it with regular each if these fancy enumerable methods are bit too much for you:
hash = { 'abc' => [], 'def' => [] }
a.each do |x|
if c.include?(x)
hash['abc'].push(x)
else
hash['def'].push(x)
end
end
Unfortunately this question turned out not to be as interesting as I was hoping. I was hoping that the problem was this:
Knowing the hash key and a value, how can I make sure the key's value is an array and that the given value is appended to that?
For instance, start with h being {}. I have a key name :k and a value 1. I want h[:k], if it doesn't already exist, to be [1]. But if it does already exist, then it's an array and I want to append 1 to that array; for instance, if h[:k] is already [3,2], now it should be [3,2,1].
I can think of various ways to ensure that, but one possibility is this:
(hash[key] ||= []) << value
To see that this works, let's make it a method:
def add_to_hash_array_value(hash:, key:, value:)
(hash[key] ||= []) << value
end
Now I'll just call that a bunch of times:
h = {}
add_to_hash_array_value(hash:h, key:"abc", value:1)
add_to_hash_array_value(hash:h, key:"abc", value:2)
add_to_hash_array_value(hash:h, key:"def", value:4)
add_to_hash_array_value(hash:h, key:"def", value:5)
add_to_hash_array_value(hash:h, key:"abc", value:3)
puts h #=> {"abc"=>[1, 2, 3], "def"=>[4, 5]}
We got the right answer.
This is nice because suppose I have some way of knowing, given a value, what key it should be appended to. I can just repeatedly apply that decision-making process.
However, the trouble with trying to apply that to the original question is that the original question seems not to know exactly how to decide, given a value, what key it should be appended to.

Read multiple hash at a time and update the values in Ruby [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
Consider the variables:
ctr = ['cobol',nil,nil,'test',nil,'cobol', nil]
h1 = {
0=>{"ABC"=>"10000100126N", "CDE"=>"2013-08-30-}", "TPP"=>"11400000206633458812N", "APD"=> "01531915972", "PRODUCTID"=>"113n", "OPP"=>"201509n", "CTC"=>"C"},
1=>{"ABC"=>"00000039540A", "CDE"=>"0182.22X", "TPP"=>"1234.565N", "APD"=>"12345600", "PRODUCTID"=>"ACHN", "OPP"=>"00000000000119964.1256", "CTC"=>"00000000000211920"}
}
h2 = {'{' => '+0', 'A' => '+1', 'B' => '+2', '}' => '-0', 'N' => '-5'}
The task is to read the ctr data and where the value is cobol, we need to apply logic for those values in h1 hash only.
we need to parse the hash h1 and if the last char in hash's value matches with one of the key in hash h2 then replace that value with the corresponding value and prepend symbol to the string.
For example: when we scan hash h1, for value "10000100126N", as the last char is N and it exists in h2, then the output should be '-100001001265' where 5 is appended and - is prepended. [Not that the ctr for this is 'cobol']
But if we look at the second value "CDE"=>"2013-08-30-}", since for this key-value pair, the ctr value is not cobol, wee do nothing with the string.
This is what i have done so far:
h1.each do |k,h|
h.update(h) do |*, v|
# puts v
h2.each do |q,p|
if (v[-1] == q)
v.sub!(v[-1], p[-1])
v.sub!(/(.*?)/, p[0] +'\1')
end
end
v
end
end
This code is updating the string as per the requirement, but its running for all the values in h1, i need to run the code only for the corresponding index where the value in the array ctr is 'cobol'
First of all a warning when you are matching Hash positions with Array indexes. In your example ['cobol',nil,nil,'test',nil,'cobol', nil] correspond with the keys ["ABC", "CDE", "TPP", "APD", "PRODUCTID", "OPP", "CTC"] from the inner Hash of h1. Keep in mind that a Hash is not index based but key based. This means, in theory the order of the hash is not maintained. A better way of doing it is by defining a Hash, like so: {"ABC"=>"cobol", "CDE"=>nil, "TPP"=>nil, "APD"=>"test", "PRODUCTID"=>nil, "OPP"=>"cobol", "CTC"=>nil}.
With this warning out of the way, let's get to the answer.
What you are looking for is the Enumerable#zip function, to combine every value with its corresponding value in ctr.
[:a, :b, :c].zip([1, 2, 3])
#=> [[:a, 1], [:b, 2], [:c, 3]]
First we need to loop through your hash, you're using Hash#each. Since this is a transformation Enumerable#map is a better fit. The map functions results in a array with transformed values. The resulting array can be transformed back into a Hash with the correct structure.
[[:a, 1], [:b, 2], [:c, 3]].to_h
#=> {:a => 1, :b => 2, :c => 3}
Here's the solution I came up with. It's not the most clean, but it works.
check_logic = lambda do |type, value|
return value unless type == 'cobol'
return value unless h2.has_key?(value[-1])
"#{h2[value[-1]][0]}#{value[0...-1]}#{h2[value[-1]][-1]}"
end
result = h1.map { |k1, v1| [k1, v1.zip(ctr).map { |(k2, v2), type| [k2, check_logic.call(type, v2)] }.to_h] }.to_h
#=> {0=>{"ABC"=>"-100001001265", "CDE"=>"2013-08-30-}", "TPP"=>"11400000206633458812N", "APD"=>"01531915972", "PRODUCTID"=>"113n", "OPP"=>"201509n", "CTC"=>"C"}, 1=>{"ABC"=>"+000000395401", "CDE"=>"0182.22X", "TPP"=>"1234.565N", "APD"=>"12345600", "PRODUCTID"=>"ACHN", "OPP"=>"00000000000119964.1256", "CTC"=>"00000000000211920"}}
As you can see I'm using zip to combine each value of the Hash with the ctr Array. I'm also using mass assignment (don't know the correct term). A simple example of this is:
(v1, v2, v3) = [1, 2, 3]
Resulting in v1 having value 1, v2 having value 2. In the second map there are 2 params, the first is an Array, containing the key and value of the inner Hash, the second is the value from the combined ctr Array. By using mass assignment I can give the key and value their own variable name.
Since the logic is a bit to much for a one liner I moved it to a lambda, but this could also be a function (when passing h2 as param).
You're trying to match an array and a hash, which is just going to cause you problems. It would be easier if you changed ctr to a hash as well:
ctr = {"ABC" => "cobol", "CDE" => nil, "TPP" => nil, "APD" => "test", "PRODUCTID" => nil, "OPP" => "cobol", "CTC" => nil}
Then at least you can match by keys.
Even better would be to start creating an object instead of using your hash. Once you start nesting collection objects this deep, it's time to create some kind of object to make your life easier.

Find the index by current sort order of an array in ruby

If there is an array
array A = ["a","b","c","d"] #Index is [0,1,2,3]
And it's sorted to.
array A = ["d","c","b","a"]
I need an array that returns me the updated index based on the sorted order
[3,2,1,0]
I'm trying to find a solution to this ruby
UPDATE to the question
If a is sorted to
array A = ["d","b","c","a"] #not a pure reverse
Then the returned index array should be
[3,1,2,0]
You need to create a mapping table that preserves the original order, then use that order to un-map the re-ordered version:
orig = %w[ a b c d ]
orig_order = orig.each_with_index.to_h
revised = %w[ d c b a ]
revised.map { |e| orig_order[e] }
# => [3, 2, 1, 0]
So long as your elements are unique this will be able to track any shift in order.
Here is one way to do this:
original_array = ["a","b","c","d"]
jumbled_array = original_array.shuffle
jumbled_array.map {|i| original_array.index(i)}
#=> [1, 3, 0, 2]
Note:
In this sample, output will change for every run as we are using shuffle to demonstrate the solution.
The solution will work only as long as array has no duplicate values.
If you do wish to solution to work with arrays with duplicate values, then, one possibility is to look at object_id of array members while figuring out the index.
jumbled_array.map {|i| original_array.map(&:object_id).index(i.object_id)}
This solution will work as long as jumbled_array contains element from original_array and no elements were recreated using dup or something that results in change in object_id values
You can use the map and index methods.
arr = ["a","b","c","d"]
sort_arr = ["d","c","b","a"]
sort_arr.map{|s| arr.index(s)}
# => [3, 2, 1, 0]

Get variable whose value is the maximum in an array

If I have an array of variables in Ruby:
a = 4
b = 7
c = 1
array = [a, b, c]
How can I get access to the name of the variable that has the highest value? (In this example b) I want to retrieve a reference to the element with the highest value, to be able to later manipulate it:
b += 10
I tried array.max but that just returns the max value 7
When you build an array by writing array = [a, b, c], the spots in the array do not retain any kind of association with the variables named a, b, and c, so you there is no way to do exactly what you are talking about. Those variables can change without affecting the array.
You could change the way you are storing your data in order to make this possible, but without knowing what data you are storing it is hard to recommend a good way to do it. You could consider storing your data inside a hash instead of in loose variables and an array:
hash = { a: 4, b: 7, c: 1}
Ruby does not support passing objects by reference, which is essentially what you are trying to do. When you call array.max, you are passed a copy of the Fixnum 7, and assignment and modification will be applied to this copy.
That is, you can not store a reference to this Array element, and modify it later. You can modify the maximum value on the spot, however, using:
array[array.index(array.max)] = array.max + 10
#=> 17
array
#=> [4, 17, 1]
Note that when there are duplicate values in the Array, array.index(array.max) will return the index of the first one.
Storing the index for later use is not a solid solution, since, while Arrays in Ruby are ordered, the Array or its elements can be modified between the point you retrieve the index, and the point where you decide to change the corresponding value.
Another answer suggests there's a hack to mimic pass-by-reference behaviour, but in doing this, you're working against the intention of the language, and it could lead to a lot of pain down the line.
If you are trying to manipulate the maximum of three values, then, you are better off doing something like this:
array.max + 10 # or any other manipulation for that matter.
If you are asking what you are asking for academic purposes, then, you could use Ruby's Binding to inspect values of local variables.
a = 4
b = 7
c = 1
array = [a, b, c]
# Select variable whose value matches with maximum of three elements
# Variable's name as symbol is obtained by this process
var = binding.send(:local_variables).select do |i|
array.max.eql? binding.local_variable_get(i)
end.first
#=> :b
# Update value of variable using symbol representing its name
binding.local_variable_set(var, binding.local_variable_get(var) + 10)
puts b
#=> 17
You could also use Binding#eval to manipulate the variable, for e.g.:
binding.eval("#{var.to_s} += 10")
#=> 17
Note that we need to use var.to_s as var contains a symbol, and we need to convert it to string to get variable name string that can be used in eval.
You can use Array#index for this..
1.9.3-p484 :008 > max_elem = array.index(array.max)
=> 1
1.9.3-p484 :009 > array[max_elem] = array[max_elem] + 10
=> 11
1.9.3-p484 :010 > array
=> [4, 17, 1]
1.9.3-p484 :011 >

What is the easiest way to push an element to the beginning of the array?

I can't think of a one line way to do this. Is there a way?
What about using the unshift method?
ary.unshift(obj, ...) → ary
Prepends objects to the front of self, moving other elements upwards.
And in use:
irb>> a = [ 0, 1, 2]
=> [0, 1, 2]
irb>> a.unshift('x')
=> ["x", 0, 1, 2]
irb>> a.inspect
=> "["x", 0, 1, 2]"
You can use insert:
a = [1,2,3]
a.insert(0,'x')
=> ['x',1,2,3]
Where the first argument is the index to insert at and the second is the value.
array = ["foo"]
array.unshift "bar"
array
=> ["bar", "foo"]
be warned, it's destructive!
Since Ruby 2.5.0, Array ships with the prepend method (which is just an alias for the unshift method).
You can also use array concatenation:
a = [2, 3]
[1] + a
=> [1, 2, 3]
This creates a new array and doesn't modify the original.
You can use methodsolver to find Ruby functions.
Here is a small script,
require 'methodsolver'
solve { a = [1,2,3]; a.____(0) == [0,1,2,3] }
Running this prints
Found 1 methods
- Array#unshift
You can install methodsolver using
gem install methodsolver
You can use a combination of prepend and delete, which are both idiomatic and intention revealing:
array.delete(value) # Remove the value from the array
array.prepend(value) # Add the value to the beginning of the array
Or in a single line:
array.prepend(array.delete(value))

Resources