Each_pair analog for array (zipping 2 arrays) - ruby

There is each_pair method allows getting pairs of hash on every loop iteration:
{ 1 => "a", 2 => "b" }.each_pair do |key, value|
# do something with #{key} and #{value}
end
How can index of current array element could be known on every loop iteration?
array.each do |element|
# what is element index in array?
end
There is a boring solution using some incrementing iterator. But that iterator
should be assigned before the loop and should be manually incremented on every
iteration. It's obviously boring.
It will be great if there is a some way to zip some array with 1.. array and
get array of tuples like ["b", "d", "e"] → [(1,"b"), (2,"d"), (3,"e")] and
than pattern matched each element of the pair in| |` statement.
So, finally, what I am looking for is some function f, that:
f(["a"]) do |index, element|
# index == 1, element == "a"
end

You can loop over an array and get the current index by using Enumerable::each_with_index

Correct me if I'm wrong, but I'm assuming that you want an array consisting of sub-arrays with the originall arrays index and value?
a= ["b", "d", "e"]
a.enum_with_index.map {|ind, val| [ind, val]
=> [[0, "b"], [1, "d"], [2, "e"]]

Related

How does `Hash#sort{|a, b| block}` work?

In ruby-doc I see this example:
h = { "a" => 20, "b" => 30, "c" => 10 }
h.sort {|a,b| a[1]<=>b[1]} #=> [["c", 10], ["a", 20], ["b", 30]]
Can anyone explain what a[1]<=>b[1] means? What are we comparing here? Is a is a key and b its value? Why we are comparing index 1?
a and b are both arrays of [key, value] which come from Hash#sort.
Converts hsh to a nested array of [ key, value ] arrays and sorts it, using Array#sort.
So a[1]<=>b[1] sorts the resulting pairs by the value. If it were a[0]<=>b[0] it would be sorting by the key.
Ruby doesn't have a key-value-pair or tuple datatype, so all Hash iteration methods (each, map, select, sort, …) represent hash entries as an Array with two elements, [key, value]. (In fact, most methods aren't even implemented in Hash, they are inherited from Enumerable and don't even know anything about keys and values.)
Meditate on this:
h = { "a" => 20, "b" => 30, "c" => 10 }
h.sort { |a,b| # => {"a"=>20, "b"=>30, "c"=>10}
a[1]<=>b[1]
} # => [["c", 10], ["a", 20], ["b", 30]]
For each loop of the key/value pairs in h, Ruby passes each key/value pair into the block as an array of two elements.

Sort hash by order of keys in secondary array

I have a hash:
hash = {"a" => 1, "b" =>2, "c" => 3, "d" => 4}
And I have an array:
array = ["b", "a", "d"]
I would like to create a new array that is made up of the original hash values that correspond with original hash keys that are also found in the original array while maintaining the sequence of the original array. The desired array being:
desired_array = [2, 1, 3]
The idea here is to take the word "bad", assign numbers to the alphabet, and then make an array of the numbers that correspond with "b" "a" and "d" in that order.
Since your question is a little unclear I'm assuming you want desired_array to be an array (you say you want a new array and finish the sentence off with new hash). Also in your example I'm assuming you want desired_array to be [2, 1, 4] for ['b', 'a', 'd'] and not [2, 1, 3] for ['b', 'a', 'c'].
You should just you the Enumerable#map method to create a array that will map the first array to the your desired array like so:
desired_array = array.map { |k| hash[k] }
You should familiarize yourself with the Enumerable#map method, it's quite the handy method. From the rubydocs for the method: Returns a new array with the results of running block once for every element in enum. So in this case we are iterating through array and invoking hash[k] to select the value from the hash and creating a new array with values selected by the hash. Since iteration is in order, you will maintain the original sequence.
I would use Enumerable#map followed by Enumerable#sort_by, for example:
hash = {"d" => 4, "b" =>2, "c" => 3, "a" => 1}
order = ["b", "a", "d"]
# For each key in order, create a [key, value] pair from the hash.
# (Doing it this way instead of filtering the hash.to_a is O(n) vs O(n^2) without
# an additional hash-probe mapping. It also feels more natural.)
selected_pairs = order.map {|v| [v, hash[v]]}
# For each pair create a surrogate ordering based on the `order`-index
# (The surrogate value is only computed once, not each sort-compare step.
# This is, however, an O(n^2) operation on-top of the sort.)
sorted = selected_pairs.sort_by {|p| order.find_index(p[0]) }
p sorted
# sorted =>
# [["b", 2], ["a", 1], ["d", 4]]
I've not turned the result back into a Hash, because I am of the belief that hashes should not be treated as having any sort of order, except for debugging aids. (Do keep in mind that Ruby 2 hashes are ordered-by-insertion.)
All you need is values_at:
hash.values_at *array
Enumerable methods map, each works perfect
desired_array = array.map { |k| hash[k] }
or
desired_array = array.each { |k| hash[k] }

How do I transpose multiple arrays unless an array element is empty?

I have the following code:
[array1, array2, array3].transpose
I want to do something like this:
[array1, array2, array3].transpose unless array2[value] == ""
I want to transpose each array, but when the value for array2 that is being transposed is blank I want to skip to the next set of values altogether, ensuring that none of the arrays get transposed.
How can I handle that within Ruby 1.9.3?
Consider this :
arr1 = ["a", "b"]
arr2 = ["", "c"]
arr3 = ["d", "e"]
Now, as per your requirement,; you want those tranposed arrays where arr2 blank value is not present.
Use #reject to do that as:
[arr1, arr2, arr3].transpose.reject{ |x| x[1].empty? }
Here, x[1] corresponds to the second element in each transposed array; and it comes from arr2; so here, we rejected all those instances where "" was present in arr2.
Hope it helps
If you had large arrays, and memory were a concern, you could reduce the size of the array before transposing it. That's straightfoward. Here's one way (i being the row containing one or more instances of bad_obj):
def tr(arr, i, bad_obj)
bad_cols = []
arr[i].each_with_index {|e,i| bad_cols << i if e==bad_obj}
arr.map {|row| row.reject.with_index {|e,i| bad_cols.include?(i)}}.transpose
end
arr = [[1,2,3], [4,'',6], [7,8,9]]
tr(arr, 1, "") # => [[1,4,7], [3,6,9]]
construct an array bad_cols that contains the indices of the columns to be skipped.
remove the bad_cols columns from each row, then apply transpose.
Edit: replaced
bad_cols = arr[i].each_with_index.reduce([]) {|a,e| a << e.last if e.first==bad_obj; a}
with the first two lines of def tr(). Simpler is better.

Create an array from a hash with each_with_index

I have an array:
arr = ["a", "b", "c"]
What I want to do is to create a Hash so that it looks like:
{1 => "a", 2 => "b", 3 => c}
I tried to do that:
Hash[arr.each_with_index.map { |item, i| [i => item] }]
but didn't get what I was looking for.
each_with_index returns the original receiver. In order to get something different from the original receiver, map is necessary anyway. So there is no need of an extra step using each or each_with_index. Also, with_index optionally takes the initial index.
Hash[arr.map.with_index(1){|item, i| [i, item]}]
# => {1 => "a", 2 => "b", 3 => c}
Hash[] takes an array of arrays as argument. So you need to use [i, item] instead of [i => item]
arr = ["a", "b", "c"]
Hash[arr.each_with_index.map{|item, i| [i+1, item] }]
#=> {1=>"a", 2=>"b", 3=>"c"}
Just for clarification: [i => item] is the same as writing [{i => item}] so you really produced an array of arrays that in turn contained a single hash each.
I also added a +1 to the index so the hash keys start at 1 as you requested. If you don't care or if you want to start at 0, just leave that off.
arr = ["a", "b", "c"]
p Hash[arr.map.with_index(1){|i,j| [j,i]}]
# >> {1=>"a", 2=>"b", 3=>"c"}

Reorder Ruby array based on the first element of each nested array

My goal is to convert a into b:
a = [["a","b"], ["d", "c"], ["a", "o"], ["d", "g"], ["c", "a"]]
b = [[["a","b"], ["a", "o"]], ["c", "a"], [["d", "c"], ["d", "g"]]
They are grouped by the first element in each nested array. So far I have:
def letter_frequency(c)
d = Hash.new(0)
c.each do |v|
d[v] += 1
end
d.each do |k, v|
end
end
def separate_arrays(arry)
arry2 = []
arry3 = []
big_arry = []
y = 0
while y < arry.length
arry2.push(arry[y][0])
arry3.push(arry[y][1])
y += 1
end
freq = letter_frequency(arry2)
front = arry.slice!(0..(freq["a"] - 1))
end
separate_arrays(a)
Not only does this seem like overkill, but there are now guarantees that "a" will be a legit Hash key, so the last part doesn't work. Thanks for any help.
You can try to do something like this:
a.group_by(&:first).values.map {|e| e.length > 1 ? e : e.flatten}
# => [[["a", "b"], ["a", "o"]], [["d", "c"], ["d", "g"]], ["c", "a"]]
I use the following methods:
Enumerable#group_by (by first element of an array, like in your question):
Returns a hash, which keys are evaluated result from the block, and
values are arrays of elements in enum corresponding to the key.
Hash#values:
Returns a new array populated with the values from hsh. See also Hash#keys.
Enumerable#map (required because you don't want to get nested array when there are only one match, like for c letter):
Returns a new array with the results of running block once for every element in enum.
Enumerable#flatten:
Returns a new array that is a one-dimensional flattening of this array
(recursively). That is, for every element that is an array, extract
its elements into the new array. If the optional level argument
determines the level of recursion to flatten

Resources