Combination up to n - ruby

Given an array a, what is the best way to achieve its combinations up to the n-th? For example:
a = %i[a b c]
n = 2
# Expected => [[], [:a], [:b], [:c], [:a, b], [:b, :c], [:c, :a]]

Do as below :
a = %w[a b c]
n = 3
0.upto(n).flat_map { |i| a.combination(i).to_a }
# => [[], ["a"], ["b"], ["c"], ["a", "b"],
# ["a", "c"], ["b", "c"], ["a", "b", "c"]]

Another way:
def all_combis(a, n, b=[])
n.zero? ? b.unshift([]) : all_combis(a, n-1, b.unshift(*a.combination(n)))
end
all_combis(%i[a b c], 0)
#=> [[]]
all_combis(%i[a b c], 1)
#=> [[], [:a], [:b], [:c]]
all_combis(%i[a b c], 2)
#=> [[], [:a], [:b], [:c], [:a, :b], [:a, :c], [:b, :c]]
all_combis(%i[a b c], 3)
#=> [[], [:a], [:b], [:c], [:a, :b], [:a, :c], [:b, :c], [:a, :b, :c]]
If order and efficiency are unimportant, this also works:
a.repeated_combination(n).map(&:uniq) << []
%i[a b c].repeated_combination(2).map(&:uniq) << []
#=> [[:a], [:a, :b], [:a, :c], [:b], [:b, :c], [:c], []]

Related

How to return a row twice inside map method

I would like to return a value twice inside a map method, only if (for example) the key == :b
I have something like this:
{a: 1, b: 2, c: 3}.map{ |x| x }
# => [[:a, 1], [:b, 2], [:c, 3]]
I would like to create this:
# => [[:a, 1], [:b, 2], [:b, 2], [:c, 3]]
I tried:
output = {a: 1, b: 2, c: 3}.map{ |x| x.first == :b ? [x,x] : x }
# => [[:a, 1], [[:b, 2], [:b, 2]], [:c, 3]]
output.flatten
# => [:a, 1, :b, 2, :b, 2, :c, 3]
output.flatten(1)
# => [:a, 1, [:b, 2], [:b, 2], :c, 3]
Any thoughts?
You can just write it in a single line
{a: 1, b: 2, c: 3}.map{ |x| x.first == :b ? [x,x] : [x] }.flatten(1)
{a: 1, b: 2, c: 3}.map{ |x| x.first == :b ? [x,x] : [x] }.flatten(1)
or
{a: 1, b: 2, c: 3}.reduce([]){ |memo, x| x.first == :b ? memo << x << x : memo << x }
or maybe a couple dozen other ways :)

Using Ruby .map Method To Combine Arrays With Unique Nested Values

I'm trying to use the map function to combine an array of arrays into an array of arrays that eliminate a unique value in arr[0][0] but pull arr[0][1] and group it with the corresponding unique value.
arr = [[a, 1], [a, 2], [b,3], [b, 4]]
=> [[a, [1, 2]], [b, [3,4]]]
I'm sure this is pretty basic but I'm rather new to coding in general. Thank you for your help.
Try this:
arr = [[:a, 1], [:a, 2], [:b, 3], [:b, 4]]
arr.group_by(&:first).map { |k, v| [k, v.map(&:last)] }
#=> [[:a, [1, 2]], [:b, [3, 4]]]
Depending on what your goal is, you might want to turn the result into a hash:
Hash[arr.group_by(&:first).map { |k, v| [k, v.map(&:last)] }]
#=> {:a=>[1, 2], :b=>[3, 4]}

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

I want a function that takes parameters like this
list = [{a:1},{a:2},{b:3},{b:4},{c:5},{a:6}]
key = :a
combine_only_sequential_occurances_of_specific_key(list,key)
and would return this
[{a:[1,2]},{b:3},{b:4},{c:5},{a:6}]
Basically, combine a list of key/value pairs that occur sequentially, but limited only to a specific key (or if you like, a set of keys) and preserve order.
Thanks to the power of Enumerable, this is a rather easy task:
def combine_only_sequential_occurances_of_specific_key(list, *keys)
list.
chunk {|h| if keys.include?(k = h.keys.first) then k else :_alone end }.
# split into chunks by key
map {|k, hs| if k == :_alone || hs.size == 1 then hs.first else {k => hs.map(&:values).reduce(:concat)} end}
# transform into hash from key to "sum" (i.e. concatenation) of the values
end
list = [{a: 1}, {a: 2}, {b: 3}, {b: 4}, {c: 5}, {a: 6}]
key = :a
combine_only_sequential_occurances_of_specific_key(list, key)
# => [{a: [1, 2]}, {b: 3}, {b: 4}, {c: 5}, {a: 6}]
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}

Dealing with many [...] in Ruby

In a pure ruby script I have this:
result = JSON.parse result.body_str
count = result && result["ke1"] && result["ke1"]["key2"] && result["ke1"]["key2"]["key3"] && result["ke1"]["key2"]["key3"]["key4"] ?
result["key1"]["key2"]["key3"]["key4"].to_i :
123
Is there any way to simplify this?
count = result["key1"]["key2"]["key3"]["key4"].to_i rescue 123
if you want to make a private method for better readability, you could do
def count(result)
result["key1"]["key2"]["key3"]["key4"].to_i
rescue NoMethodError
123
end
I add the NoMethodError to limit the errors that the rescue can swallow. Despite arguments about
using exceptions for flow control, I prefer this for readability. In a small function or one liner, it technically doesn't even change the flow, as it all remains contained in one location.
If it is used inside a tight loop with millions of records, you may want to compare with other solutions using a profiler, but you have to make that call based on the actual usage. If this is used on a bit of code that may run 5 times a day, stick with what's easier to read and maintain.
I would write it like this, and put it in a module to be included as required.
Code
def value_at_deep_key(hash, path)
path.each_with_index.reduce(hash) do |current, (segment, i) |
case c = current[segment]
when Hash then c
else (i==path.size-1) ? (current.key?(segment) ? c : :NO_MATCH) : {}
end
end
end
Examples
value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b, :c]) #=> "cat"
value_at_deep_key({a: {b: {c: false}}}, [:a, :b, :c]) #=> false
value_at_deep_key({a: {b: {c: nil}}}, [:a, :b, :c]) #=> nil
value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b]) #=> {:c=>"cat"}
value_at_deep_key({a: {b: {c: "cat"}}}, [:a]) #=> {:b=>{:c=>"cat"}}
value_at_deep_key({z: {b: {c: "cat"}}}, []) #=> {:z=>{:b=>{:c=>"cat"}}}
value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c]) #=> :NO_MATCH
value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c, :d]) #=> :NO_MATCH
One could then write:
val = value_at_deep_key(hash, path)
(val = 123) if (val == :NO_MATCH)
If the value for the last key could not be nil,
else (i==path.size-1) ? (current.key?(segment) ? c : :NO_MATCH) : {}
could be replaced with:
else (i==path.size-1) ? c : {}
in which case nil would be returned when there is no match.
I occasionally define a method like this
def value_at_deep_key hash, path, default=nil
path.inject(hash) {|current,segment| current && current[segment]} || default
end
This uses inject to grab each level of the hash in turn.
You would use like this
value_at_deep_key(result, %w(key1 key2 key3 key4), 123)
Personally I don't like the use of rescue for this sort of thing - it can mask errors.
The accepted answer doesn't work very well:
Here is the code:
def value_at_deep_key(hash, path, default=nil)
path.inject(hash) {|current,segment| current && current[segment]} || default
end
Here are some results:
1)--------------------
h = {
'key1' => {'key2' => {'key3' => {'key4' => 3}}}
}
p value_at_deep_key(h, %w[key1 key2 key3 key4], 123)
--output:--
3
2)--------------------
h = {
'key1' => 1,
'key2' => 2,
'key3' => 3,
'key4' => 4,
}
p value_at_deep_key(h, %w[key1 key2 key3 key4], 123)
--output:--
1.rb:16:in `[]': no implicit conversion of String into Integer (TypeError)
from 1.rb:16:in `block in value_at_deep_key'
from 1.rb:16:in `each'
from 1.rb:16:in `inject'
from 1.rb:16:in `value_at_deep_key'
from 1.rb:19:in `<main>'
3)---------------------
h = {
'key1' => {'key2' => {'key3' => 4}}
}
p value_at_deep_key(h, %w[key1 key2 key3 key4], 123)
--output:--
1.rb:16:in `[]': no implicit conversion of String into Integer (TypeError)
The following answer seems to work better:
def value_at_deep_key(hash, key_sequence, default=nil)
return "No keys to lookup!" if key_sequence.empty?
value = hash
key_sequence.each do |key|
case value
when Hash
value = value[key]
else
value = nil
break
end
end
value.nil? ? default : Integer(value) #A found value of nil produces the default, which is
#also the case when one of the keys doesn't exist in the Hash.
#Because to_i() will silently convert a found string with no leading numbers to 0,
#use Integer() instead, which will throw a descriptive error when trying to convert any String(or Hash or Array) to an int.
end
--output:--
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b, :c], 123) #=> `Integer': invalid value for Integer(): "cat" (ArgumentError)
p value_at_deep_key({a: {b: {c: false}}}, [:a, :b, :c], 123) #=> `Integer': can't convert false into Integer (TypeError)
p value_at_deep_key({a: {b: {c: nil}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b], 123) #=> `Integer': can't convert Hash into Integer (TypeError
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a], 123) #=> `Integer': can't convert Hash into Integer (TypeError)
p value_at_deep_key({z: {b: {c: "cat"}}}, [], 123) #=> "No keys to lookup!"
p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c, :d], 123) #=> 123
p value_at_deep_key(
{'key1' => {'key2' => {'key3' => {'key4' => "4"}}}},
%w[key1 key2 key3 key4],
default=123,
) #=> 4
p value_at_deep_key(
{ 'key1' => {'key2' => {'key3' => "4"}}},
%w[key1 key2 key3 key4],
default=123,
) #=> 123
p value_at_deep_key(
{
'key1' => "1",
'key2' => "2",
'key3' => "3",
'key4' => "4",
},
%w[key1 key2 key3 key4],
default=123,
) #=> 123
p value_at_deep_key(
{ 'key1' => {'key2' => {'key3' => {'key4' => nil}}}},
%w[key1 key2 key3 key4],
default=123,
) #=> 123
p value_at_deep_key(
{'key1' => {'key2' => {'key3' => {'key4' => 'hello'}}}},
%w[key1 key2 key3 key4],
default=123,
) #=> `Integer': invalid value for Integer(): "hello" (ArgumentError)
But maybe the following answer will suit you better:
If you must have:
A found String that looks like a number--converted to an int, or
The default
...in other words no errors, you can do this:
def value_at_deep_key(hash, key_sequence, default=nil)
value = hash
key_sequence.each do |key|
case value
when Hash
value = value[key]
else
value = hash.object_id #Some unique value to signal that the Hash lookup failed.
break
end
end
begin
value == hash.object_id ? default : Integer(value)
rescue TypeError, ArgumentError #If the Hash lookup succeeded, but the value is: nil, true/false, a String that is not all numbers, Array, Hash, an object that neither responds to to_int() nor to_i()
default
end
end
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {c: false}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {c: nil}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a, :b], 123) #=> 123
p value_at_deep_key({a: {b: {c: "cat"}}}, [:a], 123) #=> 123
p value_at_deep_key({z: {b: {c: "cat"}}}, [], 123) #=> 123
p value_at_deep_key({z: {b: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {b: {z: "cat"}}}, [:a, :b, :c], 123) #=> 123
p value_at_deep_key({a: {z: {c: "cat"}}}, [:a, :b, :c, :d], 123) #=> 123
p value_at_deep_key(
{'key1' => {'key2' => {'key3' => {'key4' => "4"}}}},
%w[key1 key2 key3 key4],
default=123,
) #=> 4
p value_at_deep_key(
{ 'key1' => {'key2' => {'key3' => "4"}}},
%w[key1 key2 key3 key4],
default=123,
) #=> 123
p value_at_deep_key(
{
'key1' => "1",
'key2' => "2",
'key3' => "3",
'key4' => "4",
},
%w[key1 key2 key3 key4],
default=123,
) #=> 123
p value_at_deep_key(
{ 'key1' => {'key2' => {'key3' => {'key4' => nil}}}},
%w[key1 key2 key3 key4],
default=123,
) #=> 123
p value_at_deep_key(
{'key1' => {'key2' => {'key3' => {'key4' => [1, 2, 3] }}}},
%w[key1 key2 key3 key4],
default=123,
) #=> 123

creating an array with information contained in two other arrays

how can I buit an array using two arrays as follow:
name = [a, b, c]
how_many_of_each [3, 5, 2]
to get
my_array = [a, a, a, b, b, b, b, b, c, c]
Use zip, flat_map, and array multiplication:
irb(main):001:0> value = [:a, :b, :c]
=> [:a, :b, :c]
irb(main):002:0> times = [3, 5, 2]
=> [3, 5, 2]
irb(main):003:0> value.zip(times).flat_map { |v, t| [v] * t }
=> [:a, :a, :a, :b, :b, :b, :b, :b, :c, :c]
name.zip(how_many_of_each).inject([]) do |memo, (x, y)|
y.times { memo << x}
memo
end
=> [:a, :a, :a, :b, :b, :b, :b, :b, :c, :c]
EDIT: Oh well, there's better, see #David Grayson.
This will do it in an easy to understand way:
my_array = []
name.count.times do |i|
how_many_of_each[i].times { my_array << name[i] }
end
array = ["a", "b", "c"]
how_many = [2, 2, 2]
result = []
array.each_with_index do |item, index|
how_many[index].times { result << item }
end
print result # => ["a", "a", "b", "b", "c", "c"]
You can pick the one you want (just swap the comment #):
class Array
def multiply_times(how_many)
r = []
#how_many.length.times { |i| how_many[i].times { r << self[i] } }
self.each_with_index { |e, i| how_many[i].times { r << e } }
r
end
end
p ['a', 'b', 'c'].multiply_times([3, 5, 2])
#=> ["a", "a", "a", "b", "b", "b", "b", "b", "c", "c"]

Resources