Combine Array of Array into All Possible Combinations, Forward Only, in Ruby

Combine array of array into all possible combinations, forward only, in Ruby

Know your Array#product:

a = [['1','2'],['a','b'],['x','y']]
a.first.product(*a[1..-1]).map(&:join)

All possible combinations from a hash of arrays in Ruby


a = {}
a[:bitrate] = ["100", "500", "1000"]
a[:fps] = ["15", "30"]
a[:qp] = ["20", "30"]

def product_hash(hsh)
attrs = hsh.values
keys = hsh.keys
product = attrs[0].product(*attrs[1..-1])
product.map{ |p| Hash[keys.zip p] }
end

product_hash(a)

you'll get

[{:bitrate=>"100", :fps=>"15", :qp=>"20"},
{:bitrate=>"100", :fps=>"15", :qp=>"30"},
{:bitrate=>"100", :fps=>"30", :qp=>"20"},
{:bitrate=>"100", :fps=>"30", :qp=>"30"},
{:bitrate=>"500", :fps=>"15", :qp=>"20"},
{:bitrate=>"500", :fps=>"15", :qp=>"30"},
{:bitrate=>"500", :fps=>"30", :qp=>"20"},
{:bitrate=>"500", :fps=>"30", :qp=>"30"},
{:bitrate=>"1000", :fps=>"15", :qp=>"20"},
{:bitrate=>"1000", :fps=>"15", :qp=>"30"},
{:bitrate=>"1000", :fps=>"30", :qp=>"20"},
{:bitrate=>"1000", :fps=>"30", :qp=>"30"}]

You can also add new key to your hash.

a = {}
a[:bitrate] = ["100", "500", "1000"]
a[:fps] = ["15", "30"]
a[:qp] = ["20", "30"]
a[:bw] = [true, false]

product_hash(a)

#=>
[{:bitrate=>"100", :fps=>"15", :qp=>"20", :bw=>true},
{:bitrate=>"100", :fps=>"15", :qp=>"20", :bw=>false},
{:bitrate=>"100", :fps=>"15", :qp=>"30", :bw=>true},
{:bitrate=>"100", :fps=>"15", :qp=>"30", :bw=>false},
{:bitrate=>"100", :fps=>"30", :qp=>"20", :bw=>true},
{:bitrate=>"100", :fps=>"30", :qp=>"20", :bw=>false},
{:bitrate=>"100", :fps=>"30", :qp=>"30", :bw=>true},
{:bitrate=>"100", :fps=>"30", :qp=>"30", :bw=>false},
{:bitrate=>"500", :fps=>"15", :qp=>"20", :bw=>true},
{:bitrate=>"500", :fps=>"15", :qp=>"20", :bw=>false},
{:bitrate=>"500", :fps=>"15", :qp=>"30", :bw=>true},
{:bitrate=>"500", :fps=>"15", :qp=>"30", :bw=>false},
{:bitrate=>"500", :fps=>"30", :qp=>"20", :bw=>true},
{:bitrate=>"500", :fps=>"30", :qp=>"20", :bw=>false},
{:bitrate=>"500", :fps=>"30", :qp=>"30", :bw=>true},
{:bitrate=>"500", :fps=>"30", :qp=>"30", :bw=>false},
{:bitrate=>"1000", :fps=>"15", :qp=>"20", :bw=>true},
{:bitrate=>"1000", :fps=>"15", :qp=>"20", :bw=>false},
{:bitrate=>"1000", :fps=>"15", :qp=>"30", :bw=>true},
{:bitrate=>"1000", :fps=>"15", :qp=>"30", :bw=>false},
{:bitrate=>"1000", :fps=>"30", :qp=>"20", :bw=>true},
{:bitrate=>"1000", :fps=>"30", :qp=>"20", :bw=>false},
{:bitrate=>"1000", :fps=>"30", :qp=>"30", :bw=>true},
{:bitrate=>"1000", :fps=>"30", :qp=>"30", :bw=>false}]

Creating permutations from a multi-dimensional array in Ruby

Yup, Array#product does just that (Cartesian product):

a = [[1,2], [3], [4,5,6]]
head, *rest = a # head = [1,2], rest = [[3], [4,5,6]]
head.product(*rest)
#=> [[1, 3, 4], [1, 3, 5], [1, 3, 6], [2, 3, 4], [2, 3, 5], [2, 3, 6]]

Another variant:

a.inject(&:product).map(&:flatten)
#=> [[1, 3, 4], [1, 3, 5], [1, 3, 6], [2, 3, 4], [2, 3, 5], [2, 3, 6]]

How to output all possible combinations using loops in Ruby?

This implementation is like counting recursively in binary:

def combinations(items)
return [] unless items.any?
prefix = items[0]
suffixes = combinations(items[1..-1])
[[prefix]] + suffixes + suffixes.map {|item| [prefix] + item }
end

> combinations(%w(a b c))
=> [["a"], ["b"], ["c"], ["b", "c"], ["a", "b"], ["a", "c"], ["a", "b", "c"]]

At each stage, the combinations are a concatenation of:

  • the first element alone
  • the combinations of the following elements (elements 1..n-1)
  • the first element combined with the combinations of the following elements

ruby - How can I generate an array of every combination of letters and numbers of a given length?


alphanum = [*?a..?z, *?0..?9]
length.times.flat_map { |l|
alphanum.repeated_permutation(l + 1).map(&:join)
}

Note that length > 3 will give you a lot of results.

EDIT: As meagar says, this is very memory-intensive. An Enumerator-based answer (not as pretty, but won't kill your memory):

e = Enumerator.new do |y|
length.times do |l|
alphanum.repeated_permutation(l + 1).each do |p|
y << p.join
end
end
end

Challenge: combine into an array only sequential keys of specific value in Ruby

Code

def combine_only_blah_blah_blah(list, key)
list.flat_map(&:to_a).
slice_when { |(k1,_),(k2,_)| k1 != k2 }.
flat_map do |a|
k = a.first.first
(a.size > 1 && k == key) ? { k=>a.map(&:last) } : a.map { |b| [b].to_h }
end
end

Example

list = [{a: 1}, {a: 2}, {b: 3}, {b: 4}, {c: 5}, {a: 6}]
key = :a

combine_only_blah_blah_blah(list, key)
#=> [{:a=>[1, 2]}, {:b=>3}, {:b=>4}, {:c=>5}, {:a=>6}]

Explanation

For list and key above, the steps are as follows.

b = list.flat_map(&:to_a)
#=> [[:a, 1], [:a, 2], [:b, 3], [:b, 4], [:c, 5], [:a, 6]]
e = b.slice_when { |(k1,_),(k2,_)| k1 != k2 }
#=> #<Enumerator: #<Enumerator::Generator:0x007f9bda968c50>:each>

We can see what elements will be generated by this enumerator by converting it to an array.

e.to_a
#=> [[[:a, 1], [:a, 2]], [[:b, 3], [:b, 4]], [[:c, 5]], [[:a, 6]]]

Continuing,

e.flat_map do |a|
k = a.first.first
(a.size > 1 && k == key) ? { k=>a.map(&:last) } : a.map { |b| [b].to_h }
end
#=> [{:a=>[1, 2]}, {:b=>3}, {:b=>4}, {:c=>5}, {:a=>6}]

The first element generated by e that is passed to flat_map's block is

a = e.next
#=> [[:a, 1], [:a, 2]]

and the block calculation is as follows.

k = a.first.first
#=> :a
(a.size > 1 && k == key)
#=> (2 > 1 && :a == :a)
#=> true

so

{ k=>a.map(&:last) }
#=> {:a=>[1, 2]}

is executed. The next element generated by e and passed to the block, and the subsequent block calculations are as follows.

a = e.next
#=> [[:b, 3], [:b, 4]]
k = a.first.first
#=> :b
(a.size > 1 && k == key)
#=> (2 > 1 && :b == :a)
#=> false
a.map { |b| [b].to_h }
#=> [{:b=>3}, {:b=>4}]

Note that when

b = [:b, 3]

[b].to_h
#=> [[:b, 3]].to_h
#=> {:b=>3}

For Ruby versions prior to v2.0, when Array#to_h made its debut, use Hash::[].

Hash[[b]]
#=> {:b=>3}

All possible operations with two operators(+, -) on items of an array

Final edit: I have removed old code as it did not do what OP wanted.

Same code but keeping the order:

The idea of the code is to create an array of modifiers.

For the example with an array of size 3 and 1 elements that should be modified the first line of code would populate the variable mod_final with the array [[-1, 0, 0], [0, -1, 0], [0, 0, -1], [1, 0, 0], [0, 1, 0], [0, 0, 1]]. Just a set of 1,0 and -1. Then in the final step we map this array and add it with the original to produce the final output.

array = [1,10,100]

def combination_plus_minus(array,number)

# Create an array of modifiers we are going to use:
mod = [-1,1].repeated_combination(number).to_a
# mod = [[-1, -1], [-1, 1], [1, 1]]

# Fill the rest of the array up with zeros:
mod_with_fill = mod.map{|x| x + [0]*(array.size-number) }
# mod_with_fill = [[-1, -1, 0], [-1, 1, 0], [1, 1, 0]]

# Map each modifier set to every permuation, then combine them all:
mod_final = mod_with_fill.flat_map{|x| x.permutation(array.size).to_a }.uniq
# mod_final = [[-1, -1, 0], [-1, 0, -1], [0, -1, -1], [-1, 1, 0], [-1, 0, 1], [1, -1, 0], [1, 0, -1], [0, -1, 1], [0, 1, -1], [1, 1, 0], [1, 0, 1], [0, 1, 1]]


#Finally take each set and add the numbers of the inital array:
mod_final.map{|x| x.zip(array).map{|x| x.reduce(:+) } }

end

p combination_plus_minus array, 2 #=> [[0, 9, 100], [0, 10, 99], [1, 9, 99], [0, 11, 100], [0, 10, 101], [2, 9, 100], [2, 10, 99], [1, 9, 101], [1, 11, 99], [2, 11, 100], [2, 10, 101], [1, 11, 101]]

If you are not happy with the order you can apply any sorting algoritm you want to the mod_final variable before it is combined with the original array.

A Ruby branching way to allow different user's input options to check for all possible valid answer stored in an array

A more idiomatic Ruby way of allowing for these different user options would be to have all possible valid user input stored in an array, then checking if the user provided a valid option by calling include? on the array. This way, you can update the array with more possible options at a later time. Generally arrays like this are stored as constants, though you can also store them as local variables. I find that for a script like this, variables are fine, but if I'm writing a larger program and I need arrays like this (for example, arrays that specify valid options) in a class, then I use constants to make it easy for other contributors to find them. Another option yet would be to have a configuration file (can be a Ruby file, yaml file, JSON file...) that defines valid options, and then you can load this file when executing your program.

Here is an example of what I mean:

VALID_AFFIRMATIVE_ANSWERS = %w(yes yup yea positive)
VALID_NEGATIVE_ANSWERS = %w(no not nay negative)

answer = gets.chomp
if VALID_AFFIRMATIVE_ANSWERS.include?(answer)
puts "Good!"
puts desc_text
my_var = gets.chomp.to_i
if my_var == 3736
puts good_text
else
puts wrong_text
puts bad_text
end
elsif VALID_NEGATIVE_ANSWERS.include?(answer)
puts bad_text
else
puts yes_no
end

I'm not sure what errors you're receiving, but I'm not sure if you're ever defining some of these variables (for example desc_text etc.).



Related Topics



Leave a reply



Submit