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
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 :)
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]}
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}
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
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"]