Merge Ruby Arrays into Multidimensional Array - ruby

What is the best way to merge the following two arrays into a multidimensional array?
x = ['A', 'B', 'C']
y = ['D', 'E', 'F']
Desired result:
z = [['A', 'D'], ['A', 'E'], ['A', 'F'], ['B', 'D'], ['B', 'E'], ['B', 'F'], ['C', 'D'], ['C', 'E'], ['C', 'F']]

You can use Array#product:
x = ['A', 'B', 'C']
y = ['D', 'E', 'F']
result = x.product(y)
puts result.inspect

Here's one way, although not necessarily the simplest possible way:
x = ['A', 'B', 'C']
y = ['D', 'E', 'F']
result = []
x.each do |x|
y.each do |y|
result << [x, y]
end
end
puts result.inspect
Update: here's a more concise way:
x = ['A', 'B', 'C']
y = ['D', 'E', 'F']
puts x.map { |x|
y.map { |y| [x, y] }
}.inspect

Another way of doing it is like this:
x = ['A', 'B', 'C']
y = ['D', 'E', 'F']
print x.concat(y).each_slice(2).to_a # => [["A", "B"], ["C", "D"], ["E", "F"]]

Related

Permutation of arrays

There are multiple sets of elements like:
[['a', 'b', 'c'], ['e', 'f'], ['g', 'h', 'i']]
I need an algorithm to get all possible combinations of each element from each set.
E.g.
['a', 'e', 'g']
['a', 'f', 'g']
['a', 'f', 'h']
['a', 'f', 'i']
['b', 'e', 'g']
...etc
You can use backtracking to solve this:
def get_perms(arr):
def backtrack(idx, partial_res, res):
if idx == len(arr):
res.append(partial_res[:])
return
for i in range(0, len(arr[idx])):
partial_res.append(arr[idx][i])
backtrack(idx+1, partial_res, res)
partial_res.pop()
res = []
backtrack(0, [], res)
return res
A quick test:
arr = [['a', 'b', 'c'], ['e', 'f'], ['g', 'h', 'i']]
get_perms(arr)
[['a', 'e', 'g'],
['a', 'e', 'h'],
['a', 'e', 'i'],
['a', 'f', 'g'],
['a', 'f', 'h'],
['a', 'f', 'i'],
['b', 'e', 'g'],
['b', 'e', 'h'],
['b', 'e', 'i'],
['b', 'f', 'g'],
['b', 'f', 'h'],
['b', 'f', 'i'],
['c', 'e', 'g'],
['c', 'e', 'h'],
['c', 'e', 'i'],
['c', 'f', 'g'],
['c', 'f', 'h'],
['c', 'f', 'i']]
The algorithm just goes over each inner list and adds each element to partial_res and calls itself recursively while incrementing the index to go to the next inner list.
What you want is a Cartesion product. Use itertools:
from itertools import product
matrix = [['a', 'b', 'c'], ['e', 'f'], ['g', 'h', 'i']]
for res in product(*matrix):
print(list(res))

Check if array already contains new set regardless of order

I have an array of arrays containing objects:
[ [A, B, C],
[A, B, D],
[B, C, D] ]
I want to check that a value like [B, A, C] can't be added since it's not unique for my purposes. The existing arrays within the array shouldn't have any duplicates (I'm already handling that).
I tried the following code but it's not working:
#if false, don't add to existing array
!big_array.sort.include? new_array.sort
What am I doing wrong?
require 'set'
a = [['a', 'b', 'c'],
['a', 'b', 'd'],
['b', 'c', 'd']]
as = a.map(&:to_set)
as.include? ['b', 'a', 'c'].to_set #=> true
as.include? ['b', 'a', 'e'].to_set #=> false
Use:
(as << row.to_set) unless as.include? row.to_set
then when finished:
as.to_a
In view of your comment, if you add all your rows to a:
a = [['a', 'b', 'c'],
['a', 'b', 'd'],
['b', 'c', 'd'],
['a', 'c', 'b'],
['c', 'a', 'b'],
['e', 'a', 'b'],
['c', 'b', 'd']]
then:
a.reverse
.map(&:to_set)
.uniq
.map(&:to_a)
#=> [["b", "c", "d"],
# ["e", "a", "b"],
# ["a", "b", "c"],
# ["a", "b", "d"]]
reverse is needed to keep your original arrays, but note that ordering is not preserved in the result. If you wish to keep the ordering of the modified a:
a.each_with_object(Set.new) { |row,set| set << row.to_set }
.map(&:to_a)
#=> [["a", "b", "c"],
# ["a", "b", "d"],
# ["b", "c", "d"],
# ["e", "a", "b"]]
You should be sorting the arrays inside your big array. Not the big array itself
!big_array.map(&:sort).include? new_array.sort
a = [
['a', 'b', 'c'],
['a', 'b', 'd'],
['b', 'c', 'd']
]
class Array
def add_only_if_combination_does_not_exist_in(double_array)
if double_array.map(&:sort).include?(self.sort)
puts "Won't be added since it already exists!"
else
puts 'Will be added'
double_array << self
end
end
end
['b', 'a', 'c'].add_only_if_combination_does_not_exist_in(a)
['b', 'a', 'f'].add_only_if_combination_does_not_exist_in(a) #=> Will be added
p a #=> [["a", "b", "c"], ["a", "b", "d"], ["b", "c", "d"], ["b", "a", "f"]]
If you don't care about the order of the elements, consider using the Set class.
require 'set'
big_set = Set.new
big_set << Set.new(['a', 'b', 'c'])
# => #<Set: {#<Set: {"a", "b", "c"}>}>
big_set << Set.new(['c', 'b', 'a'])
# => #<Set: {#<Set: {"a", "b", "c"}>}>
big_set << Set.new(['d', 'a', 'b'])
# => #<Set: {#<Set: {"a", "b", "c"}>, #<Set: {"d", "a", "b"}>}>

Ruby, reorder an array based on another array? [duplicate]

This question already has answers here:
How do I quickly reorder a Ruby Array given an order?
(3 answers)
Closed 8 years ago.
I'd like to order ary according to the indices specified in order.
# Ruby
ary = ['a', 'b', 'c', 'd']
order = [2, 3, 0, 1]
# Result I want
ary = ['c', 'd', 'a', 'b']
ary = ['a', 'b', 'c', 'd']
order = [2, 3, 0, 1]
ary.values_at(*order)
#=> ["c", "d", "a", "b"]
You could do something like this:
ary = ['a', 'b', 'c', 'd']
order = [2, 3, 0, 1]
sorted_array = []
order.each do |i|
sorted_array.push(ary[i])
end
Though looking at it, Cary's answer is nicer.
ary = ['a', 'b', 'c', 'd']
order = [2, 3, 0, 1]
new_array = []
order.each do |index| #This returns the array you want.
new_array << ary[index]
end

How to get specific value within an array of arrays

I have an array (outside array) that contains three arrays (inside arrays), each of which have three elements.
array = [[a, b, c], [d, e, f], [g, h, i]]
I want to select the specific inside array using an index of the outside array and then select the value within the selected inside array based off its index. Here is what I tried:
array.each_index{|i| puts "letter: #{array[i[3]]} " }
I was hoping that would give me the following output
letter: c letter: f letter: i
but instead, I get
letter: [[a, b, c], [d, e, f], [g, h, i]]
I also tried
array.each_index{|i| puts "letter: #{array[i][3]} " }
but I get the same result. Please any suggestions are very appreciated. I need a simple explanation.
each_index is an Enumerable which goes through all indices and performs an action on each one. When it's done it will return your original collection as it's not its job to change it. If you want to output stuff on the screen via puts / print then each_index is fine.
If you want to create a new collection as a result of going through all the elements of an original collection, you should use map.
e.g.
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
new_array = array.map {|el| el[2]}
=> ["c", "f", "i"]
array.map iterates through array's elements so in every step |el| is an element, not an index, as in: ['a', 'b', 'c'] in the first iteration, ['d', 'e', 'f'] in the second one and so on...
Just pointing this out since I don't know what's the goal of what you're trying to do.
do it like this:
array.each_index{|i| puts "letter: #{array[i][2]} " }
Since you want letter at index 2, not 3.
Also array should be defined like this:
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
You could use map like so:
a = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
a.map(&:last)
# => ["c", "f", "i"]
Or if you really want the puts and not the collected values:
a.each {|v| puts "letter: #{v.last}"}
You could also use Ruby's Matrix class if there's more Matrix-y stuff you want to do:
require 'matrix'
m = Matrix[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
last_column = m.column_vectors.last
# => Vector["c", "f", "i"]
You can now use any of the Vector methods on last_column, including to_a, which will put you back in familiar territory with the last column's values.
Arrays in ruby are indexed from 0, not from 1. So:
array.each_index{|i| puts "letter: #{array[i][2]} " }
should give you what you want.
You could try the below also:
p RUBY_VERSION
arr = [[1,2,3],[4,5,6],[11,12,13]]
arr.each{|x| p x; x.each_index{|i| p "Digit :: #{x[i]}" if i == 2} }
output:
"2.0.0"
[1, 2, 3]
"Digit :: 3"
[4, 5, 6]
"Digit :: 6"
[11, 12, 13]
"Digit :: 13

How do I swap array elements using parallel assignment?

I am trying to swap two elements in an array like this
deck = []
(deck << (1..52).to_a << 'A' << 'B').flatten!
p deck
deck[deck.index("A")], deck[deck.index("B")] = deck[deck.index("B")], deck[deck.index("A")] #swap "A" and "B"
p deck
But it doesnt swap. If i do this, however:
deck[52], deck[53] = deck[53], deck[52]
it works. Any suggestions?
To make things simple, let deck be just ['A', 'B']. Here is step-by-step evaluation:
deck = ['A', 'B']
deck[deck.index("A")], deck[deck.index("B")] = deck[deck.index("B")], deck[deck.index("A")] # deck == ['A', 'B']
deck[deck.index("A")], deck[deck.index("B")] = deck[1], deck[0] # deck == ['A', 'B']
deck[deck.index("A")], deck[deck.index("B")] = 'B', 'A' # deck == ['A', 'B']
deck[0], deck[deck.index("B")] = 'B', 'A' # deck == ['A', 'B']
# Applying first assignment.
..., deck[deck.index("B")] = ..., 'A' # deck == ['B', 'B']
# NOTE: deck.index("B") is 0 now, not 1!
..., deck[0] = ..., 'A' # deck == ['B', 'B']
# Applying second assignment.
... # deck == ['A', 'B']
So what your code actually does is just assinging twise to the same element of array.
In order to fix this issue, just save deck.index() values to temporary arrays:deck = []
(deck << (1..52).to_a << 'A' << 'B').flatten!
p deck
index_a, index_b = deck.index("A"), deck.index("B")
deck[index_a], deck[index_b] = deck[index_b], deck[index_a]
p deck

Resources