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
What's the Best/Easiest Gui Library for Ruby
How to Run a Single Test/Spec File in Rspec
What Is Your Preferred Way to Produce Charts in a Ruby on Rails Web Application
How to Install Ruby 2.1.4 on Ubuntu 14.04
Breaking Up Long Strings on Multiple Lines in Ruby Without Stripping Newlines
How to Run Only Specific Tests in Rspec
Ruby Design Pattern: How to Make an Extensible Factory Class
Remove Duplicate Elements from Array in Ruby
How to Test If Parameters Exist in Rails
How to Elegantly Rename All Keys in a Hash in Ruby
What Exactly Is Arel in Rails 3.0
Rspec: "Array.Should == Another_Array" But Without Concern for Order
Rails - Redirecting Console Output to a File
How to Implement a "Callback" in Ruby
What's the Difference Between Rspec's Subject and Let? When Should They Be Used or Not