I need to create a new Hash object using two arrays.
But, the conditions is first array value should be a key value for the Hash and second array value should be the Hash value.
a = ["x", "y"]
b = [2, 4]
Result should be: c = {"x" => 2, "y" => 4}
irb(main):001:0> a = ["x", "y"]; b = [2, 4]
=> [2, 4]
irb(main):002:0> Hash[a.zip(b)]
=> {"x"=>2, "y"=>4}
Related
My hash is grumpy. :(
I want to sort it but it doesn't want, and I can't find the reason of it:
x = [{2 => "two", 1 => "one"}, {4 => "four", 3 => "three"}]
x.each do |y|
if y.is_a?(Hash) then y = y.sort end
end
(the hash has to be sorted in a .each method)
In the end, instead of having this structure:
[{2=>"two", 1=>"one"}, {4=>"four", 3=>"three"}]
I want this structure:
[[[1, "one"], [2, "two"]], [[3, "three"], [4, "four"]]]
After reading some similar questions I tried to replace Hash by ::Hash or convert the hash into an array before sorting it but it still doesn't work...
How can I sort my hash?
arr = [{2=>"two", 1=>"one"}, {4=>"four", 3=> "three"}]
arr.map { |h| h.sort_by(&:first) }
#=> [[[1, "one"], [2, "two"]], [[3, "three"], [4, "four"]]]
Your loop is grumpy not your hash :)
You are sorting the hash, but your loop does not update the array.
Here is a simplified example
array.each do |y|
y = y.sort
end
Above reassigns the sorted hash to the local variable y and does thus not update the value in array. Assignment to the block parameter does not write back to the array that you are enumerating over.
Use map instead
array = array.map do |y|
y.sort
end
The answer of Cary Swoveland is the short one
here I write a longer version
x = [{ 2 => "two", 1 => "one"}, {4 => "four", 3 => "three"}]
y = x.map{ |el| el.to_a.sort }
so the variables should be like
x => [{ 2 => "two", 1 => "one"}, {4 => "four", 3 => "three"}]
y => [[[1, "one"], [2, "two"]], [[3, "three"], [4, "four"]]]
array = [1,2,3,4,5]
array1 = array
array2 = array.dup
puts array1 == array2
Why do we have a dup method when we can just assign to another variable?
array = [1,2,3,4,5]
array1 = array
array2 = array.dup
array << "aha"
p array1 # => [1, 2, 3, 4, 5, "aha"]
p array2 # => [1, 2, 3, 4, 5]
You're fooling yourself by:
Trying to reason from a single example.
Comparing the wrong things.
Array has its own == method that compares element by element so given:
a = [ 11 ]
b = [ 11 ]
then a == b is true even though a and b reference different arrays.
In general, = simply copies a reference similar to this in C:
int *i, *j;
i = j;
but dup makes a (shallow) copy.
If you compare the object_ids:
puts array1.object_id == array2.object_id
you'll see that the underlying array objects are different even though == says that the have equal contents.
A statement like:
array1 = array
just assigns a reference to array1 from array. This means that both array and array1 point to the same memory location. If you change the underlying array, it will be reflected in both copies:
irb(main):001:0> array = [1,2,3]
=> [1, 2, 3]
irb(main):002:0> array1 = array
=> [1, 2, 3]
irb(main):003:0> array
=> [1, 2, 3]
irb(main):004:0> array1
=> [1, 2, 3]
irb(main):005:0> array[0] = 10
=> 10
irb(main):006:0> array
=> [10, 2, 3]
irb(main):007:0> array1
=> [10, 2, 3]
If you use dup, it clones the underlying data, creating new, independent storage:
irb(main):008:0> array2 = array.dup
=> [10, 2, 3]
irb(main):009:0> array
=> [10, 2, 3]
irb(main):010:0> array2
=> [10, 2, 3]
irb(main):011:0> array2[0] = 20
=> 20
irb(main):012:0> array
=> [10, 2, 3]
irb(main):013:0> array2
=> [20, 2, 3]
While each does not change the values of a given row in a 2d array (unlike map):
test_array = [[0,0],[0,0],[0,0]]
test_array[0].each {|e| e = 1}
test_array # => [[0, 0], [0, 0], [0, 0]]
test_array[0].map! {|e| e = 1}
test_array # => [[1, 1], [0, 0], [0, 0]]
each changes the values of a given column (in a way different from map):
test_array = [[0,0],[0,0],[0,0]]
test_array.each {|row| row[0] = 2}
test_array # => [[2, 0], [2, 0], [2, 0]]
test_array.map! {|row| row[0] = 2}
test_array # => [2, 2, 2]
Can somebody explain what is happening?
That's because, in Ruby, parameters are 'passed by object reference'.
In the case below,
test_array.each {|row| row[0] = 2}
row refers to an array inside test_array, and modifying its contents will reflect in test_array. This mutates the sub-array present in test_array.
In the case below,
test_array[0].each {|e| e = 1}
e refers to an integer in array[0], and making it point to a different integer will not impact the original value as we are not mutating the integer values present in array[0].
More details here: Is Ruby pass by reference or by value?
Regarding your query on map, please note that you are using map!, which will mutate the object on which the method is called, whereas map returns the value.
map! {|item| block } → ary
Invokes the given block once for each element of self, replacing the element with the value returned by the block.
Output of map is returned as is:
test_array.map {|row| row[0] = 2}
test_array # => [[2, 0], [2, 0], [2, 0]]
test_array is replaced with output of map:
test_array.map! {|row| row[0] = 2}
test_array # => [2, 2, 2]
I would like to add an element to an array but without actually changing that array and instead it returning a new one. In other words, I want to avoid:
arr = [1,2]
arr << 3
Which would return:
[1,2,3]
Changing arr itself. How can I avoid this and create a new array?
You can easily add two arrays in Ruby with plus operator. So, just make an array out of your element.
arr = [1, 2]
puts arr + [3]
# => [1, 2, 3]
puts arr
# => [1, 2]
it also works by extending arr using * operator
arr = [1,2]
puts [*arr, 3]
=> [1, 2, 3]
I'm trying to build a hash with:
hash = {}
strings = ["one", "two", "three"]
array = [1, 2, 3, 4, 5, 6]
so that I end up with:
hash = { "one" => [1, 2] ,
"two" => [3, 4] ,
"three" => [5, 6] }
I have tried:
strings.each do |string|
array.each_slice(2) do |numbers|
hash[string] = [numbers[0], numbers[1]]
end
end
But that yields:
hash = { "one" => [5,6] , "two" => [5,6], "three" => [5,6] }
I know why it does this (nested loops) but I don't know how to achieve what I'm looking for.
If you want a one-liner:
hash = Hash[strings.zip(array.each_slice(2))]
For example:
>> strings = ["one", "two", "three"]
>> array = [1, 2, 3, 4, 5, 6]
>> hash = Hash[strings.zip(array.each_slice(2))]
=> {"one"=>[1, 2], "two"=>[3, 4], "three"=>[5, 6]}
hash = {}
strings.each { |string| hash[string] = array.slice!(0..1) }
This is a solution using methods and techniques you seem familiar with. It is not a 'one liner' solution but if you are new might be more understandable for you. The first answer is very elegant though.
As Mu says, Zip method is the best choose:
Converts any arguments to arrays, then merges elements of self with corresponding elements from each argument. This generates a sequence of self.size n-element arrays, where n is one more that the count of arguments. If the size of any argument is less than enumObj.size, nil values are supplied. If a block is given, it is invoked for each output array, otherwise an array of arrays is returned.