combining three arrays [closed] - ruby

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have three arrays of arrays. I essentially need them laid on top of each other.
first = [[111, 1], [222, 2], [333, 3]]
second = [[111, 4], [222, 5], [333, 6]]
third = [[111,7], [222, 8], [333, 9]]
Ideally it would be great if the final array looked like this:
final = [[111, 1, 4, 7], [222, 2, 5, 8], [333, 3, 6, 9]]
I looked at the product method in hopes that that could help but no go. I have also tried to loop over all three but I guess I'm not that smart.

Combine them, then group them, then map them to fit your specs:
(first + second + third).group_by(&:first).map { |k, v| [k, *v.map(&:last)] }

Not sure if this is working but I done it at the phone so... And for sure there are better ways to achieve it
first = [[111, 1], [222, 2], [333, 3]]
second = [[111, 4], [222, 5], [333, 6]]
third = [[111,7], [222, 8], [333, 9]]
all = [first, second, third]
hash = {}
all.each do |arr|
arr.each do |elem|
hash[elem[0]] ||= []
hash[elem[0]] << elem[1]
end
end
Array.new hash.map { |k,v| [k, *v]}
For more elems
hash[elem[0]].concat elem[1..-1]

[first,second,third].transpose.map do |array|
array.reduce { |init,e| init << e.last }
end
=> [[111, 1, 4, 7], [222, 2, 5, 8], [333, 3, 6, 9]]
To deal with several last elements in array:
[first,second,third].transpose.map do |array|
array.reduce { |init,e| init + e.drop(1) }
end

Related

Ruby: how to split a collection (Enumerable) in multiple (>2) partitions by some predicate?

Is there a function, to partition a collection (Enumerable) by some non-boolean predicate into more than 2 partitions? (my math knowledge got rusty - but I think, this was called "factorize" - or at least in German "Faktorisierung")
This follows my own yesterday question (Ruby: how to split a collection by predicate into two collections in ruby?)
and I wonder, is there another standard ruby method I just missed.
Here´s an example, showing the principle:
def factorize(eee)
h={}; h.default_proc= proc{|h,k| h[k]=[] }
eee.each{|e| k=yield e; h[k]<<e }
h.values
end
factorize( 1..10 ){|n| n%3 } #=> [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]
You can use group_by: https://apidock.com/ruby/v2_5_5/Enumerable/group_by
Example:
(1..10).group_by { |n| n % 3 }.values
=> [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]
While the Enumerable#group_by solution is clearly the answer, it may be instructive to streamline your original attempt using #each_with_object.
def factorize(eee)
eee.each_with_object({}) do |e, hsh|
k = yield e
hsh[k] ||= []
hsh[k] << e
end.values
end
factorize(1..10) { |x| x % 3 }
# => [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]

create array with iterative assignment

Would like to exploit the following behaviour in Ruby
ary = Array.new(5) { |i|
[i, j=2*i, k=j+1]
}
p ary #> [[0, 0, 1], [1, 2, 3], [2, 4, 5], [3, 6, 7], [4, 8, 9]]
It works for my purposes, but I couldn't find in the language definition whether this is legal Ruby. Is it? Or is it likely to break in the future?
[Edit] A smaller working example raising the same issue is
i = 1
ary = [i, j=2*i, k=j+1]
p ary #> [1, 2, 3]
But of course this example only has theoretical relevance contrary to the first, which does have practical relevance.

Problems using `elsif` in a `sort` block

I have this array:
ary = [[1, 6, 7], [1, 4, 9], [1, 8, 3]]
I want to sort it by the first odd number, or the last number if they are all even, in each subarray.
Since the first element in each array is the same object 1 for this particular ary, I can solve this like this:
ary2 = ary.sort_by { |a, b, c| b.odd? ? b : c }
But when I try a more general one:
arr2 = ary.sort_by { |a, b, c| a.odd? ? a : b.odd? ? b : c }
ary2 comes back unsorted.
I tried removing the ternary operators like this:
ary2 = ary.sort_by do |a, b, c|
if a.odd?
a
elsif b.odd?
b
else
c
end
end
with the same effect (i.e., none).
Is there some reason that elsif can't be used in blocks passed to the sort_by method?
Edit: Axiac pointed out the problem with my logic. It looks like conditional logic has to deal with all of the possible permutations of odd and even values. This works:
arr2 = arr.sort_by do |a, b, c|
if a.odd?
if b.odd?
if c.odd?
[a, b, c]
else
[a, b]
end
elsif c.odd?
[a, c]
else
a
end
elsif b.odd?
if c.odd?
[b, c]
else
b
end
else
c
end
end
Maybe there's a more succinct and less brittle way to do it, but it's probably a good idea to do it this way instead:
arr2 = arr.sort_by do |sub_arr|
temp = sub_arr.select do |e|
e.odd?
end
temp.empty? ? Array(sub_arr.last) : temp
end
I'll see myself out.
Regarding your original question, just as axiac points out in the comment, the result of the sorting should be exactly the same as the input array because they are all sorted by the first odd element in each subarray, which is 1, and the sort method is stable in MRI.
Regarding your question after the edit, my answer would be:
ary.sort_by{|a| a[0...-1].select(&:odd?) << a.last}
# => [[1, 8, 3], [1, 6, 7], [1, 4, 9]]
I am pretty confident that this is what you wrote after the edit that you wanted, but I am not sure if this is what you wanted since the sorting mechanism looks strange to me.
I find the statement of the question ambiguous. I will give an answer that is consist with one interpretation. If that is not what you want, please clarify hte question.
def my_sort(arr)
arr.sort_by {|a| a.any?(&:odd?) ? a.map {|e| e.odd? ? e : Float::INFINITY} : [a.last]}
end
my_sort [[1, 6, 7], [1, 4, 9], [1, 2, 3]]
#=> [[1, ∞, 7], [1, ∞, 9], [1, ∞, 3]] (sort_by)
#=> [[1, 2, 3], [1, 6, 7], [1, 4, 9]]
my_sort [[3, 6, 7], [4, 1, 9], [5, 8, 1]]
#=> [[3, ∞, 7], [∞, 1, 9], [5, ∞, 1]] (sort_by)
#=> [[3, 6, 7], [5, 8, 1], [4, 1, 9]]
my_sort [[2, 6, 8], [4, 1, 4], [8, 6, 2]]
#=> [[8], [∞, 1, ∞], [2]] (sort_by)
#=> [[8, 6, 2], [2, 6, 8], [4, 1, 4]]
my_sort [[8, 6, 2], [5, 1, 1], [6, 8, 4]]
#=> [[2], [5, 1, 1], [4] (sort_by)
#=> [[8, 6, 2], [6, 8, 4], [5, 1, 1]]
For each example I've shown the arrays used by sort_by to produce the sort shown on the following line.

Inserting elements into new array and then deleting from old array, some elements getting ignored

I'm trying to remove pairs of the smallest and largest elements from an Array and store them in a second one. Is there a better way to do this or a Ruby method I don't know about that could accomplish something like this?
Here's my code:
nums = [1, 2, 3, 4, 5, 6]
pairs = []; for n in nums
pairs << [n, nums.last]
nums.delete nums.last
nums.delete n
end
Current result:
nums
#=> [2, 4]
pairs
#=> [[1, 6], [3, 5]]
Expected result:
nums
#=> []
pairs
#=> [[1, 6], [2, 5], [3, 4]]
Assuming nums is sorted and can be modified, I like this way because it has a mechanical feel about it:
pairs = (nums.size/2).times.map { [nums.shift, nums.pop] }
#=> [[1, 6], [2, 5], [3, 4]]
nums
#=> []
I see #Drenmi has the same idea of using shift and pop.
If you don't want to modify nums, you could of course operate on a copy.
Enumerating over an Array while deleting it's content is generally not advisible. Here's an alternative solution:
nums = *(1..6)
#=> [1, 2, 3, 4, 5, 6]
pairs = []
#=> []
until nums.size < 2 do
pairs << [nums.shift, nums.pop]
end
pairs
#=> [[1, 6], [2, 5], [3, 4]]

Subract values of 2 arrays and get a new array with subtracted values

Basically, I want to do this:
[1,2,3,4,5].some_method([1,5,8,7,8])
=> [0,-3,-5,-3,-3]
What is the best way to do this in Ruby?
[1,2,3,4,5].zip([1,5,8,7,8]).map { |a, b| a - b }
p [1,2,3,4,5].zip([1,5,8,7,8]).map{|el| el.first-el.last}
#=> [0, -3, -5, -3, -3]
.zip combines both arrays like [[1, 1], [2, 5], [3, 8], [4, 7], [5, 8]]. With .map there isnt much work left for the block - just subtract the 2 values.
[1,2,3,4,5].zip([1,5,8,7,8]).map{|x|x[0]-x[-1]}
or the usual array iteration
0.upto(a.size-1).map{|x| a[x]-b[x] }

Resources