I have two arrays like this:
["1","7","8","10"]
and
["1","2","3","6","9","11"]
These arrays represents ids from a class called Place that a user selected. I want to select the places ids with most votes. I tried transpose but as the arrays have different sizes, they cannot be transposed.
The expected output for this example is:
{ "1" => 2, "7" => 1, "8" => 1, "10" => 1, "2" => 1, "3" => 1, "6" => 1, "9" => 1, "11" => 1 }
You can join all arrays and calculate the number of identical elements like this:
arrays = [["1","7","8","10"], ["1","2","3","6","9","11"]].reduce(:+)
arrays.inject(Hash.new(0)) { |memo, e| memo.update(e => memo[e] + 1) }
# "{ "1" => 2, "7" => 1, "8" => 1, "10" => 1, "2" => 1, "3" => 1, "6" => 1, "9" => 1, "11" => 1 }"
Once you have this intermediate result use max_by to select the key with the max value from the hash:
arrays = [["1","7","8","10"], ["1","2","3","6","9","11"]].reduce(:+)
arrays.inject(Hash.new(0)) { |memo, e| memo.update(e => memo[e] + 1) }
.max_by { |_, count| count }[0]
#=> "1"
This is another way:
arr = [["1","7","8","10"], ["1","2","3","6","9","11"], ["1","2","7"]]
h = arr.flatten.sort_by(&:to_i).group_by(&:itself)
h.update(h) { |_,v| v.size }
#=> {"1"=>3, "2"=>2, "3"=>1, "6"=>1, "7"=>2, "8"=>1, "9"=>1, "10"=>1, "11"=>1}
The steps:
a = arr.flatten
#=> ["1", "7", "8", "10", "1", "2", "3", "6", "9", "11", "1", "2", "7"]
b = a.sort_by(&:to_i)
#=> ["1", "1", "1", "2", "2", "3", "6", "7", "7", "8", "9", "10", "11"]
h = b.group_by(&:itself)
#=> {"1"=>["1", "1", "1"], "2"=>["2", "2"], "3"=>["3"], "6"=>["6"],
# "7"=>["7", "7"], "8"=>["8"], "9"=>["9"], "10"=>["10"], "11"=>["11"]}
If you are using a version of Ruby prior to 2.2 (when Object#itself was introduced) you will need to instead write:
h = b.group_by { |s| s }
Lastly:
h.update(h) { |_,v| v.size }
#=> {"1"=>["1", "1", "1"], "2"=>["2", "2"], "3"=>["3"], "6"=>["6"],
# "7"=>["7", "7"], "8"=>["8"], "9"=>["9"], "10"=>["10"], "11"=>["11"]}
This uses the form of Hash#update (aka merge!) that employs a block (here { |_,v| v.size }) to determine the values of keys that are present in both hashes being merged (which in this case is all of the keys).
Update: the method Hash#transform_values made its debut in Ruby v2.4. This allows us to write the following.
arr.flatten.
sort_by(&:to_i).
group_by(&:itself).
transform_values(&:size)
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
A String s comprised of numbers from 0-9 contains a perfect substring if all the elements within a substring occurs exactly k times. Calculate the number of perfect substrings in s.
EXAMPLE
s = '1102021222'
k = 2
Here s contains 6 substrings:
11
0202
110202
102021
22
22
Can anyone help me with the solution to this in Ruby with the least complexity?
TIA..
Code
def count_em(str, k)
enum = str.each_char
(k..str.size).step(k).sum do |n|
enum.each_cons(n).count do |a|
a.tally.all? { |_k,v| v == k }
end
end
end
Examples
str = '1102021222'
count_em(str, 1) #=> 18 ( 1, 1, 0, 2, 0, 2, 1, 2, 2, 2,
# 10, 02, 20, 02, 21, 12, 102, 021
count_em(str, 2) #=> 6 (11, 22, 22, 0202, 110202, 102021)
count_em(str, 3) #=> 1 (102021)
count_em(str, n) #=> 0 for n > 3
Explanation
Enumerable#tally made its debut in Ruby v2.7. To support earlier versions of Ruby replace tally with
each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }
Enumerable#sum was new in Ruby 2.4. To support earlier versions of Ruby replace sum with
reduce(:+)
See also Enumerable#each_cons, Enumerable#count and Enumerable#all? and Hash::new.
Note that no strings having k instances of all unique digits contained in the string could be produced for values of n that are not multiples of k. It is for that reason that step(k) is present.
The easiest way to explain the calculations is to salt the method with puts statements and run it.
def count_em(str, k)
puts "k = #{k}"
enum0 = str.each_char
puts "enum0 = #{enum0}"
(k..str.size).step(k).sum do |n|
puts "n = #{n}"
enum1 = enum0.each_cons(n)
puts " enum1.to_a = #{enum1.to_a}"
enum1.count do |a|
puts " a = #{a}"
h = a.tally
puts " a.tally = #{h}"
puts " a.tally.all? {|_k,v| v == k} = #{h.all? {|_k,v| v==k}}"
h.all? { |_k,v| v==k }
end
end
end
The line puts " enum1.to_a = #{enum1.to_a}" merely displays the values that will be generated by the enumerator enum1 and passed to count's block.
Try it for k = 2:
count_em(str, 2)
#=> 6
displays the following:
k = 2
enum0 = #<Enumerator:0x00007f9467ab60f0>
n = 2
enum1.to_a = [["1", "1"], ["1", "0"], ["0", "2"], ["2", "0"],
["0", "2"], ["2", "1"], ["1", "2"], ["2", "2"],
["2", "2"]]
a = ["1", "1"]
a.tally = {"1"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["1", "0"]
a.tally = {"1"=>1, "0"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2"]
a.tally = {"0"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "0"]
a.tally = {"2"=>1, "0"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2"]
a.tally = {"0"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "1"]
a.tally = {"2"=>1, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "2"]
a.tally = {"1"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "2"]
a.tally = {"2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["2", "2"]
a.tally = {"2"=>2}
a.tally.all? {|_k,v| v == k} = true
n = 4
enum1.to_a = [["1", "1", "0", "2"], ["1", "0", "2", "0"],
["0", "2", "0", "2"], ["2", "0", "2", "1"],
["0", "2", "1", "2"], ["2", "1", "2", "2"],
["1", "2", "2", "2"]]
a = ["1", "1", "0", "2"]
a.tally = {"1"=>2, "0"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "0", "2", "0"]
a.tally = {"1"=>1, "0"=>2, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "0", "2"]
a.tally = {"0"=>2, "2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["2", "0", "2", "1"]
a.tally = {"2"=>2, "0"=>1, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "1", "2"]
a.tally = {"0"=>1, "2"=>2, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "1", "2", "2"]
a.tally = {"2"=>3, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "2", "2", "2"]
a.tally = {"1"=>1, "2"=>3}
a.tally.all? {|_k,v| v == k} = false
n = 6
enum1.to_a = [["1", "1", "0", "2", "0", "2"], ["1", "0", "2", "0", "2", "1"],
["0", "2", "0", "2", "1", "2"], ["2", "0", "2", "1", "2", "2"],
["0", "2", "1", "2", "2", "2"]]
a = ["1", "1", "0", "2", "0", "2"]
a.tally = {"1"=>2, "0"=>2, "2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["1", "0", "2", "0", "2", "1"]
a.tally = {"1"=>2, "0"=>2, "2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["0", "2", "0", "2", "1", "2"]
a.tally = {"0"=>2, "2"=>3, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "0", "2", "1", "2", "2"]
a.tally = {"2"=>4, "0"=>1, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "1", "2", "2", "2"]
a.tally = {"0"=>1, "2"=>4, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
n = 8
enum1.to_a = [["1", "1", "0", "2", "0", "2", "1", "2"],
["1", "0", "2", "0", "2", "1", "2", "2"],
["0", "2", "0", "2", "1", "2", "2", "2"]]
a = ["1", "1", "0", "2", "0", "2", "1", "2"]
a.tally = {"1"=>3, "0"=>2, "2"=>3}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "0", "2", "0", "2", "1", "2", "2"]
a.tally = {"1"=>2, "0"=>2, "2"=>4}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "0", "2", "1", "2", "2", "2"]
a.tally = {"0"=>2, "2"=>5, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
n = 10
enum1.to_a = [["1", "1", "0", "2", "0", "2", "1", "2", "2", "2"]]
a = ["1", "1", "0", "2", "0", "2", "1", "2", "2", "2"]
a.tally = {"1"=>3, "0"=>2, "2"=>5}
a.tally.all? {|_k,v| v == k} = false
Write a method that groups the above hash into 2 groups of 'even' and 'odd' length using 'inject'.
input - "['abc','def','1234','234','abcd','x','mnop','5','zZzZ']"
My code listed below already works. But I want to know better way to do it using default value for hash's key. I meant to say something like below -
h=Hash.new { |hash, key| hash[key] = []}
Solution :
class Array
def group_even_odd
key_hash = group_by(&:length)
key_array = %w(even odd)
key_hash.each_with_object('odd' => [], 'even' => []) do |(key, value), even_odd_hash|
even_odd_hash[key_array[key % 2]].push(value)
even_odd_hash
end
end
end
if ARGV.empty?
puts 'Please provide an input'
else
input = ARGV[0].scan(/\w+/).map(&:to_s)
puts input.group_even_odd
end
Expected and actual are same, code is working.
Expected result -
{"odd"=>[["abc", "def", "234"], ["x", "5"]], "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
One possible option, given
ary = ["abc", "def", "1234", "234", "abcd", "x", "mnop", "5", "zZzZ"]
First group by even odd, then group by size:
ary.group_by { |e| e.size.even? ? 'even' : 'odd' }
.transform_values { |v| v.group_by(&:size).values }
#= {"odd"=>[["abc", "def", "234"], ["x", "5"]], "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
First step, to explain:
ary.group_by { |e| e.size.even? ? 'even' : 'odd' }
#=> {"odd"=>["abc", "def", "234", "x", "5"], "even"=>["1234", "abcd", "mnop", "zZzZ"]}
Then Hash#transform_values grouping each by size.
The following does not meet the requirement that inject (aka reduce) be used, but it is how I would do it.
arr = ['abc', 'def', '1234', '234', 'abcd', 'x', 'mnop', '5', 'zZzZ']
odd, even = arr.each_with_object(Hash.new { |h,k| h[k]=[] }) do |s,h|
h[s.size] << s
end.
values.
partition { |a| a.first.size.odd? }
#=> [[["abc", "def", "234"], ["x", "5"]],
# [["1234", "abcd", "mnop", "zZzZ"]]]
{ "odd"=>odd, "even"=>even }
#=> {"odd"=>[["abc", "def", "234"], ["x", "5"]],
# "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
The steps are as follows.
h = arr.each_with_object(Hash.new {|h,k| h[k]=[]}) do |s,h|
h[s.size] << s
end
#=> {3=>["abc", "def", "234"], 4=>["1234", "abcd", "mnop", "zZzZ"],
# 1=>["x", "5"]}
a = h.values
#=> [["abc", "def", "234"], ["1234", "abcd", "mnop", "zZzZ"],
# ["x", "5"]]
odd, even = a.partition { |a| a.first.size.odd? }
#=> [[["abc", "def", "234"], ["x", "5"]],
# [["1234", "abcd", "mnop", "zZzZ"]]]
{ "odd"=>odd, "even"=>even }
#=> {"odd"=>[["abc", "def", "234"], ["x", "5"]],
# "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
If one insists on fitting a square peg into a round hold (using inject/reduce), I suppose that could be done as follows.
arr.reduce({ "odd"=>[], "even"=>[] }) do |g,s|
oe = s.size.odd? ? "odd" : "even"
i = g[oe].find_index { |a| a.any? && a.first.size == s.size }
case i.nil?
when true then g[oe] << [s]
else g[oe][i] << s
end
g
end
#=> {"odd"=>[["abc", "def", "234"], ["x", "5"]],
# "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
Preferably in Ruby
I need a way to determine if one array is a "subarray" of another array when order matters
For instance,
a = ["1", "4", "5", "7", "10"]
b = ["1", "4", "5"]
c = ["1", "5", "4"]
d = ["1", "5", "10"]
a includes b = true
a includes c = false
a include d = false
Thanks in advance.
[b, c, d].map do |arr|
a.each_cons(arr.length).any?(&arr.method(:==))
end
#⇒ [true, false, false]
This is definitely not the most performant solution, but for not huge arrays is works and, which is important, is readable.
Enumerable#each_cons.
a = ["1", "4", "5", "7", "10"]
b = ["1", "4", "5"]
c = ["1", "5", "4"]
d = ["1", "5", "10"]
def sub_set?(arr_a, arr_b)
arr_a.select.with_index do |a, index|
arr_b[index] == a
end == arr_b
end
puts "testing sub_set #{a.inspect} and #{c.inspect}"
puts sub_set?(a,c).inspect
class Array
def includes(array)
return false if array.size > self.size
indexes = []
self.each_with_index {|e, i| indexes << i if e == array[0]}
p indexes
indexes.each do |i|
return true if self[i..i+array.size-1] == array
end
return false
end
end
p a.includes b
p a.includes c
p a.includes d
Or less phpish :)
class Array
def includes(array)
return false if array.size > self.size
indexes = each.with_index.select { |e, i| e == array[0] }.map(&:last)
indexes.each {|i| self[i..i+array.size-1] == array ? (return true) : (return false)}
end
end
p a.includes b
p a.includes c
p a.includes d
You could join the elements with an arbitrary character then compare strings:
a.join(' ').match?(array.join(' '))
This works for the test cases you have provided:
a.join(' ').match?(b.join(' '))
#=> true
a.join(' ').match?(c.join(' '))
#=> false
a.join(' ').match?(d.join(' '))
#=> false
But this is not a general solution and will fail on varying types of arrays (see comments for further discussion).
Given the expression
1 * 2 / 3 + 4
I'm trying to write out all possible variants of the equation when implementing order of operations. Such as:
(1 * 2) / 3) + 4
and
(1 * (2 / 3)) + 4
This is what I have now:
expression = [1.0, "+", 2.0, "+", 3.0, "+", 4.0]
storage = []
exp1 = expression #Make new object because insert method will overwrite expression object
storage << exp1.insert(0, "((").insert(4, ")").insert(7, ")")
But exp1's changes reflect in expression as well. I even made them in separate methods but the changes keep reflecting on expression.
How do I keep the expression object unchanged? I need to configure the original expression 5 times in total, but I'm struggling to keep it clean when I use insert. Reading the Ruby docs, the insert method is the only one suitable to what I'm doing.
Do I have to use a different data structure to store 1 * 2 / 3 + 4 other than an array?
Consider using #dup so that you don't alter the original set:
expression.dup.insert(0, "((").insert(4, ")").insert(7, ")")
Code
def all_equations(expression)
nums = expression.select { |o| o.kind_of? Numeric }.map(&:to_s)
ops = expression.select { |o| o.is_a? String }.uniq
ops.permutation(ops.size).to_a.map { |ao|
([nums.first] + ao.zip(nums[1..-1]).map(&:join)).join }
end
Examples
all_equations [1.0, "*", 2.0, "/", 3.0, "+", 4.0]
#=> ["1.0*2.0/3.0+4.0", "1.0*2.0+3.0/4.0", "1.0/2.0*3.0+4.0",
# "1.0/2.0+3.0*4.0", "1.0+2.0*3.0/4.0", "1.0+2.0/3.0*4.0"]
a = all_equations [1.0, "*", 2.0, "/", 3.0, "+", 4.0, "-", 4.0, "**", 5.0]
#=> ["1.0*2.0/3.0+4.0-4.0**5.0", "1.0*2.0/3.0+4.0**4.0-5.0",
# ...
# "1.0*2.0+3.0**4.0/4.0-5.0", "1.0*2.0+3.0**4.0-4.0/5.0",
# ...
# "1.0**2.0-3.0+4.0*4.0/5.0", "1.0**2.0-3.0+4.0/4.0*5.0"]
a.size
#=> 120
Explanation
The steps are as follows.
expression = [1.0, "*", 2.0, "/", 3.0, "*", 3.0] # note `"*"` appears twice
nums = expression.select { |o| o.kind_of? Numeric }.map(&:to_s)
#=> ["1.0", "2.0", "3.0", "3.0"]
ops = expression.select { |o| o.is_a? String }
#=> ["*", "/", "*"]
a = ops.uniq
#=> ["*", "/"]
b = a.permutation(a.size)
#=> #<Enumerator: ["*", "/"]:permutation(2)>
c = b.to_a
#=> [["*", "/"], ["/", "*"]]
c.map { |ao| ([nums.first] + ao.zip(nums[1..-1]).map(&:join)).join }
#=> ["1.0*2.0/3.0", "1.0/2.0*3.0"]
uniq has effect only when ops contains duplicates.
Extension
The following permits the numbers to be reordered as well. (I initially thought this was a requirement, which is the only reason I am offering this modification.)
expression = [1.0, "*", 2.0, "/", 3.0, "+", 4.0]
nums = expression.select { |o| o.kind_of? Numeric }.map(&:to_s)
#=> ["1.0", "2.0", "3.0", "4.0"]
ops = expression.select { |o| o.is_a? String }
#=> [["*", "/", "+"], ["*", "+", "/"], ["/", "*", "+"],
# ["/", "+", "*"], ["+", "*", "/"], ["+", "/", "*"]]
anums, aops = nums.permutation(nums.size).to_a.uniq, ops.permutation(ops.size).to_a.uniq
anums
#=> [["1", "2", "3", "4"], ["1", "2", "4", "3"], ["1", "3", "2", "4"],
# ["1", "3", "4", "2"], ["1", "4", "2", "3"], ["1", "4", "3", "2"],
# ["2", "1", "3", "4"], ["2", "1", "4", "3"], ["2", "3", "1", "4"],
# ["2", "3", "4", "1"], ["2", "4", "1", "3"], ["2", "4", "3", "1"],
# ["3", "1", "2", "4"], ["3", "1", "4", "2"], ["3", "2", "1", "4"],
# ["3", "2", "4", "1"], ["3", "4", "1", "2"], ["3", "4", "2", "1"],
# ["4", "1", "2", "3"], ["4", "1", "3", "2"], ["4", "2", "1", "3"],
# ["4", "2", "3", "1"], ["4", "3", "1", "2"], ["4", "3", "2", "1"]]
aops
#=> [["*", "/", "+"], ["*", "+", "/"], ["/", "*", "+"],
# ["/", "+", "*"], ["+", "*", "/"], ["+", "/", "*"]]
a = anums.product(aops).map { |an,ao|
([an.first] + ao.zip(an[1..-1]).map(&:join)).join }
#=> ["1*2/3+4", "1*2+3/4", "1/2*3+4",..., "4/3+2*1", "4+3*2/1", "4+3/2*1"]
a.size
#=> 144
I have some array
>> a = ["a..c", "0..2"]
=> ["a..c", "0..2"]
I need convert this array to another array
>> b = ("a".."c").to_a + (0..2).to_a
=> ["a", "b", "c", 0, 1, 2]
How I can do it?
a.flat_map do |string_range|
from, to = string_range.split("..", 2)
(from =~ /^\d+$/ ? (from.to_i..to.to_i) : (from..to)).to_a
end
#=> => ["a", "b", "c", 0, 1, 2]
what about this?
a = ["a..c", "0..2"]
b = a.map { |e| Range.new( *(e).split('..') ).to_a }.flatten
no flat_map used so it works the same on all versions
as #steenslag correctly mentioned, this version does not convert to integers.
here is a version that does:
b = a.map do |e|
Range.new( *(e).split('..').map{ |c| c =~ /\A\d+\Z/ ? c.to_i : c } ).to_a
end.flatten
see it in action here
a = ["a..c", "0..2"]
b = a.flat_map{|str| Range.new(*str.split('..')).to_a} # => ["a", "b", "c", "0", "1", "2"]
p b.map!{|v| Integer(v) rescue v} # => ["a", "b", "c", 0, 1, 2]