array to hash on ruby - ruby

I have code like this:
combinedbooks = [[
"0000A|0000B",
"0000A|0000D",
"0000B|0000D"
]]
h = Hash[combinedbooks.map {|x| [x, 1]}]
The result is:
{["0000A|0000B", "0000A|0000D", "0000B|0000D"]=>1}
What I want to have is the following:
{["0000A|0000B"]=>1, ["0000A|0000D"]=>1, ["0000B|0000D"]=>1}
I cant figure out whats the problem, I believe that there is a problem with the array declaration but im not sure about it

If you want the keys to be single-item arrays, make them arrays:
arr = ["0000A|0000B", "0000A|0000D", "0000B|0000D"]
Hash[arr.map { |x| [[x], 1] }]
# => {["0000A|0000B"]=>1, ["0000A|0000D"]=>1, ["0000B|0000D"]=>1}
The format of your input has changed to a doubly-nested array. If that's accurate, simply use my solution, but map the first element of your array instead of the top-most array:
combinedbooks = [[ "0000A|0000B", "0000A|0000D", "0000B|0000D" ]]
Hash[combinedbooks[0].map { |x| [[x], 1] }]
# => {["0000A|0000B"]=>1, ["0000A|0000D"]=>1, ["0000B|0000D"]=>1}

Im not sure how to just push elements of one array into another without creating an array of arrays
There are two ways people usually append to an array, and it's important to understand the difference between them. Meditate on these:
Append/push an array to another array using << results in a sub-array:
foo = []
foo << [1]
foo # => [[1]]
Concatenate/Add an array to another array using += results in the elements of the second array being appended, not the array itself:
foo = []
foo += [1]
foo # => [1]

Related

Can't understand why this code to convert a simple array into a simple hash isn't working

Code attached below.
arrayy = [[1,'one'],[2,'two'],[3,'three']]
hashy = {}
i = 0
arrayy.each do
hashy[arrayy[i,0]] = arrayy [i,1]
i = i+1
end
puts hashy[1]
puts hashy[2]
puts hashy[3]
end
This code doesn't output anything. No errors. So, I'm guessing that the problem is that nothing is being added to the hash.
Not sure what you are trying to achieve here, but when you are doing arrayy[i,0] in the loop, you are saying that you want to grab zero elements.
When you pass in two numbers as the argument against an array, the first number is the index of the target value and the second number is the length. For example:
arr = ['a', 'b', 'c', 'd', 'e']
puts arr[2, 3]
This would put out ['c','d','e'], which is 'starting from the element with index 2, grab 3 elements'.
You're requesting zero elements, which is an empty array, so you're keying everything on an empty array and all elements collide. To fix that, just use the iterator, as each gives you the elements you need:
arrayy = [[1,'one'],[2,'two'],[3,'three']]
hashy = {}
arrayy.each do |key, value|
hashy[key] = value
end
p hashy
# => {1=>"one", 2=>"two", 3=>"three"}
In your code the actual result you're getting is this:
p hashy
# => {[]=>[[3, "three"]]}
Where here you can see the [] key being used. The p method is really handy for looking at the internal structure of something. p hashy is equivalent to puts hashy.inspect.
As Sergio points out you were probably referencing the arrays the wrong way. To navigate two levels deep you do this:
hashy[arrayy[i][0]] = arrayy [i][1]
Where [i,0] means "at index of the array i select the next 0 elements" whereas [i][0] means "at the array at index i select the value at index 0".
It's worth noting that the simplest solution is to use Array#to_h which already does this:
arrayy = [[1,'one'],[2,'two'],[3,'three']]
hashy = array.to_h
p hashy
# => {1=>"one", 2=>"two", 3=>"three"}

Complicated ruby inject method

Can't seem to figure this out. Please help me understand what this code is requesting for regarding a variable and what the intended output is supposed to be. Thanks in advance!
def function_name(a)
a.inject({}){ |a,b| a[b] = a[b].to_i + 1; a}.\
reject{ |a,b| b == 1 }.keys
end
Assuming a is an array,
The function first count the occurrences of the keys.
a = ['a', 'b', 'c', 'b']
a.inject({}) { |a,b|
# a: a result hash, this is initially an empty hash (`{}` passed to inject)
# b: each element of the array.
a[b] = a[b].to_i + 1 # Increase count of the item
a # The return value of this block is used as `a` argument of the block
# in the next iteration.
}
# => {"a"=>1, "b"=>2, "c"=>1}
Then, it filter items that occur multiple times:
...reject{ |a,b|
# a: key of the hash entry, b: value of the hash entry (count)
b == 1 # entry that match this condition (occurred only once) is filtered out.
}.keys
# => ["b"]
So, function names like get_duplicated_items should be used instead of function_name to better describe the purpose.
It wants a to be an array, but it doesn't seem to matter what the array is made up of so you'll need some other clue to know what should be in the array.
What the code does is fairly straight foreword. For each item in the array it uses it as a key in a hash. It then basically counts how many times it sees that key. Finally it removes all of the items that only showed up once.
It returns the unique items in the array a that show up 2 or more times.

Looping and appending hashes to an array

I want to create an array of hashes like this:
[
{"start"=>1, "end"=>2},
{"start"=>2, "end"=>3},
{"start"=>3, "end"=>4},
{"start"=>4, "end"=>5},
{"start"=>5, "end"=>6}
]
When I try this code:
foo = 1
bar = 2
hash = {}
array = []
5.times do
hash['start'] = foo
hash['end'] = bar
array << hash
foo += 1
bar += 1
end
the hash values change inside array while looping and hashes are added to it. array becomes:
[
{"start"=>5, "end"=>6},
{"start"=>5, "end"=>6},
{"start"=>5, "end"=>6},
{"start"=>5, "end"=>6},
{"start"=>5, "end"=>6}
]
Why does this happen when:
foo = 1
array = []
5.times do
array << foo
foo += 1
end
array # => [1, 2, 3, 4, 5]
does not change the numeral inside array during the loop?
That is because a hash is mutable. if you have foo = {"start" => 1}, and do foo["start"] += 1, then, foo still points to the same hash although it is modified to {"start" => 2}. It does not change the reference. If you have multiple copies of this same object in an array and change modify one of them, then all of them will be modified.
On the other hand, a numeral is not mutable; if you had foo = 1, and do foo += 1, then foo will now point to 2, which is a different object from 1.
You could create a new hash each time.
foo = 1
array = []
5.times do
array << { 'start' => foo, 'end' => foo + 1 }
foo += 1
end
Use:
array << hash.dup
instead of:
array << hash
because of you've added here just references to hash, not the hashs themselves.
You should change your code to create a new hash in every loop iteration:
foo = 1
bar = 2
array = []
5.times do
hash = {}
hash['start'] = foo
hash['end'] = bar
array << hash
foo += 1
bar += 1
end
puts array
Otherwise you are always changing the same object, that's the reason you end with the same hash as array elements.
As a quick literature on subject taken from here:
Ruby variables hold references to objects and the = operator copies
the references. Also, a self assignment such as a += b is actually
translated to a = a + b. Therefore it may be advisable to be aware
whether in a certain operation you are actually creating a new object
or modifying an existing one.
For example, string << "another" is faster than string += "another"
(no extra object creation), so you would be better off using any
class-defined update-method (if that is really your intention), if it
exists. However, notice also the "side effects" on all other variables
that refer to the same object:
a = 'aString'
c = a
a += ' modified using +='
puts c # -> "aString"
a = 'aString'
c = a
a << ' modified using <<'
puts c # -> "aString modified using <<"

Comparing two arrays ignoring element order in Ruby

I need to check whether two arrays contain the same data in any order.
Using the imaginary compare method, I would like to do:
arr1 = [1,2,3,5,4]
arr2 = [3,4,2,1,5]
arr3 = [3,4,2,1,5,5]
arr1.compare(arr2) #true
arr1.compare(arr3) #false
I used arr1.sort == arr2.sort, which appears to work, but is there a better way of doing this?
The easiest way is to use intersections:
#array1 = [1,2,3,4,5]
#array2 = [2,3,4,5,1]
So the statement
#array2 & #array1 == #array2
Will be true. This is the best solution if you want to check whether array1 contains array2 or the opposite (that is different). You're also not fiddling with your arrays or changing the order of the items.
You can also compare the length of both arrays if you want them to be identical in size:
#array1.size == #array2.size && #array1 & #array2 == #array1
It's also the fastest way to do it (correct me if I'm wrong)
Sorting the arrays prior to comparing them is O(n log n). Moreover, as Victor points out, you'll run into trouble if the array contains non-sortable objects. It's faster to compare histograms, O(n).
You'll find Enumerable#frequency in Facets, but implement it yourself, which is pretty straightforward, if you prefer to avoid adding more dependencies:
require 'facets'
[1, 2, 1].frequency == [2, 1, 1].frequency
#=> true
If you know that there are no repetitions in any of the arrays (i.e., all the elements are unique or you don't care), using sets is straight forward and readable:
Set.new(array1) == Set.new(array2)
You can actually implement this #compare method by monkey patching the Array class like this:
class Array
def compare(other)
sort == other.sort
end
end
Keep in mind that monkey patching is rarely considered a good practice and you should be cautious when using it.
There's probably is a better way to do this, but that's what came to mind. Hope it helps!
The most elegant way I have found:
arr1 = [1,2,3,5,4]
arr2 = [3,4,2,1,5]
arr3 = [3,4,2,1,5,5]
(arr1 - arr2).empty?
=> true
(arr3 - arr2).empty?
=> false
You can open array class and define a method like this.
class Array
def compare(comparate)
to_set == comparate.to_set
end
end
arr1.compare(arr2)
irb => true
OR use simply
arr1.to_set == arr2.to_set
irb => true
Here is a version that will work on unsortable arrays
class Array
def unordered_hash
unless #_compare_o && #_compare_o == hash
p = Hash.new(0)
each{ |v| p[v] += 1 }
#_compare_p = p.hash
#_compare_o = hash
end
#_compare_p
end
def compare(b)
unordered_hash == b.unordered_hash
end
end
a = [ 1, 2, 3, 2, nil ]
b = [ nil, 2, 1, 3, 2 ]
puts a.compare(b)
Use difference method if length of arrays are the same
https://ruby-doc.org/core-2.7.0/Array.html#method-i-difference
arr1 = [1,2,3]
arr2 = [1,2,4]
arr1.difference(arr2) # => [3]
arr2.difference(arr1) # => [4]
# to check that arrays are equal:
arr2.difference(arr1).empty?
Otherwise you could use
# to check that arrays are equal:
arr1.sort == arr2.sort

Ruby 'tap' method - inside assignment

Recently I discovered that tap can be used in order to "drily" assign values to new variables; for example, for creating and filling an array, like this:
array = [].tap { |ary| ary << 5 if something }
This code will push 5 into array if something is truthy; otherwise, array will remain empty.
But I don't understand why after executing this code:
array = [].tap { |ary| ary += [5] if something }
array remains empty. Can anyone help me?
In the first case array and ary point to the same object. You then mutate that object using the << method. The object that both array and ary point to is now changed.
In the second case array and ary again both point to the same array. You now reassign the ary variable, so that ary now points to a new array. Reassigning ary however has no effect on array. In ruby reassigning a variable never effects other variables, even if they pointed to the same object before the reassignment.
In other words array is still empty for the same reason that x won't be 42 in the following example:
x = 23
y = x
y = 42 # Changes y, but not x
Edit: To append one array to another in-place you can use the concat method, which should also be faster than using +=.
I want to expand on this a bit:
array = [].tap { |ary| ary << 5 if something }
What this does (assuming something is true-ish):
assigns array to [], an empty array.
array.object_id = 2152428060
passes [] to the block as ary. ary and array are pointing to the same array object.
array.object_id = 2152428060
ary.object_id = 2152428060
ary << 5 << is a mutative method, meaning it will modify the receiving object. It is similar to the idiom of appending ! to a method call, meaning "modify this in place!", like in .map vs .map! (though the bang does not hold any intrinsic meaning on its own in a method name). ary has 5 inserted, so ary = array = [5]
array.object_id = 2152428060
ary.object_id = 2152428060
We end with array being equal to [5]
In the second example:
array = [].tap{ |ary| ary += [5] if something }
same
same
ary += 5 += is short for ary = ary + 5, so it is first modification (+) and then assignment (=), in that order. It gives the appearance of modifying an object in place, but it actually does not. It creates an entirely new object.
array.object_id = 2152428060
ary.object_id = 2152322420
So we end with array as the original object, an empty array with object_id=2152428060 , and ary, an array with one item containing 5 with object_id = 2152322420. Nothing happens to ary after this. It is uninvolved with the original assignment of array, that has already happened. Tap executes the block after array has been assigned.

Resources