how to convert line 1 to line 2
h1 = {"one"=>[1,2,3,4], "two"=>[4,5,6,7], "three"=>[7,8,9,1]}
h2 ={4=>["one","two"],7=>["two","three"],1=>["one","three"]}
h1.each_with_object(Hash.new { |h,k| h[k] = [] }) do |(k,v), h|
v.each { |n| h[n] << k }
end.select { |_,v| v.size > 1 }
#=> {1=>["one", "three"], 4=>["one", "two"], 7=>["two", "three"]}
This uses the form of Hash::new that takes a block and no argument. If h = Hash.new { |h,k| h[k] = [] } then, possibly after key/value pairs have been added to h, if h does not have a key n, h[n] << n causes h[n] = [] to be executed prior to h[n] << k.
I can best explain the calculations by sale code with puts statements and then executing the code for the example.
h1.each_with_object(Hash.new { |h,k| h[k] = [] }) do |(k,v), h|
puts "\nk = #{k}, v = #{v}, h = #{h}"
v.each do |n|
puts " n = #{n}"
puts " Execute h[#{n}] = [] because h does not have a key #{k}" unless
h.key(n) unless h.key?(n)
h[n] << k
puts " h after h[#{n}] << #{k} = #{h}"
end
end
.tap { |h| puts "\nh before select = #{h}" }
.select { |_,v| v.size > 1 }
#=> {1=>["one", "three"], 4=>["one", "two"], 7=>["two", "three"]}
displays:
k = one, v = [1, 2, 3, 4], h = {}
n = 1
Execute h[1] = [] because h does not have a key one
h after h[1] << one = {1=>["one"]}
n = 2
Execute h[2] = [] because h does not have a key one
h after h[2] << one = {1=>["one"], 2=>["one"]}
n = 3
Execute h[3] = [] because h does not have a key one
h after h[3] << one = {1=>["one"], 2=>["one"], 3=>["one"]}
n = 4
Execute h[4] = [] because h does not have a key one
h after h[4] << one = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one"]}
k = two, v = [4, 5, 6, 7], h = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one"]}
n = 4
h after h[4] << two = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one", "two"]}
n = 5
Execute h[5] = [] because h does not have a key two
h after h[5] << two = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one", "two"],
5=>["two"]}
n = 6
Execute h[6] = [] because h does not have a key two
h after h[6] << two = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one", "two"],
5=>["two"], 6=>["two"]}
n = 7
Execute h[7] = [] because h does not have a key two
h after h[7] << two = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one", "two"],
5=>["two"], 6=>["two"], 7=>["two"]}
k = three, v = [7, 8, 9, 1], h = {1=>["one"], 2=>["one"], 3=>["one"],
4=>["one", "two"], 5=>["two"], 6=>["two"], 7=>["two"]}
n = 7
h after h[7] << three = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one", "two"],
5=>["two"], 6=>["two"], 7=>["two", "three"]}
n = 8
Execute h[8] = [] because h does not have a key three
h after h[8] << three = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one", "two"],
5=>["two"], 6=>["two"], 7=>["two", "three"], 8=>["three"]}
n = 9
Execute h[9] = [] because h does not have a key three
h after h[9] << three = {1=>["one"], 2=>["one"], 3=>["one"], 4=>["one", "two"],
5=>["two"], 6=>["two"], 7=>["two", "three"], 8=>["three"], 9=>["three"]}
n = 1
h after h[1] << three = {1=>["one", "three"], 2=>["one"], 3=>["one"],
4=>["one", "two"], 5=>["two"], 6=>["two"], 7=>["two", "three"], 8=>["three"],
9=>["three"]}
h before select = {1=>["one", "three"], 2=>["one"], 3=>["one"], 4=>["one", "two"],
5=>["two"], 6=>["two"], 7=>["two", "three"], 8=>["three"], 9=>["three"]}
Related
I have a hash:
a = {"Q1"=>1, "Q2"=>2, "Q5"=>3, "Q8"=>3}
I want to retrieve a set of keys from it such that the sum of their values equals a certain number, for example 5. In such case, the output should be:
Q2 Q5
Please help me on how to get this.
def find_combo(h, tot)
arr = h.to_a
(1..arr.size).find do |n|
enum = arr.combination(n).find { |e| e.map(&:last).sum == tot }
return enum.map(&:first) unless enum.nil?
end
end
h = {"Q1"=>1, "Q2"=>2, "Q5"=>3, "Q8"=>3}
find_combo(h, 5) #=> ["Q2", "Q5"]
find_combo(h, 2) #=> ["Q2"]
find_combo(h, 6) #=> ["Q5", "Q8"]
find_combo(h, 4) #=> ["Q1", "Q5"]
find_combo(h, 8) #=> ["Q2", "Q5", "Q8"]
find_combo(h, 9) #=> ["Q1", "Q2", "Q5", "Q8"]
find_combo(h, 10) #=> nil
Just out of curiosity:
hash = {"Q1"=>1, "Q2"=>2, "Q5"=>3, "Q8"=>3}
arr = hash.to_a
1.upto(hash.size).
lazy.
find do |i|
res = arr.combination(i).find do |h|
h.map(&:last).sum == 5
end
break res if res
end.tap { |result| break result.to_h if result }
#⇒ {"Q2" => 2, "Q5" => 3}
I am trying to transform a given string into a hash with each its character = key and index = value.
For example, if I have str = "hello", I would like it to transform into {"h"=>0, "e"=>1, "l"=>2, "l"=>3, "o"=>4}.
I created a method as such:
def map_indices(arr)
arr.map.with_index {|el, index| [el, index]}.to_h
end
#=> map_indices('hello'.split(''))
#=> {"h"=>0, "e"=>1, "l"=>3, "o"=>4}
The problem is it skips the first l. If I reverse the order of el and index: arr.map.with_index {|el, index| [index, el]}.to_h, I get all the letters spelled out: {0=>"h", 1=>"e", 2=>"l", 3=>"l", 4=>"o"}
But when I invert it, I get the same hash that skips one of the l's.
map_indices('hello'.split('')).invert
#=> {"h"=>0, "e"=>1, "l"=>3, "o"=>4}
Why is this behaving like such? How can I get it to print {"h"=>0, "e"=>1, "l"=>2, "l"=>3, "o"=>4}?
It can be done, but will confuse other Ruby programmers.A normal hash treats a key "a" as identical to another "a". Unless a little known feature .compare_by_identity is used:
h = {}.compare_by_identity
"hello".chars.each_with_index{|c,i| h[c] = i}
p h # => {"h"=>0, "e"=>1, "l"=>2, "l"=>3, "o"=>4}
Any of the following could be used. For
str = "hello"
all return
{"h"=>[0], "e"=>[1], "l"=>[2, 3], "o"=>[4]}
str.each_char
.with_index
.with_object({}) { |(c,i),h| (h[c] ||= []) << i }
See String#each_char, Enumerator#with_index and Enumerator#with_object. The block variables have been written to exploit array decomposition.
str.each_char
.with_index
.with_object(Hash.new { |h,k| h[k] = [] }) { |(c,i),h| h[c] << i }
See the form of Hash::new that takes a block and no argument. If a hash has been defined
h = Hash.new { |h,k| h[k] = [] }
and later
h[c] << i
is executed, h[c] is first set equal to an empty array if h does not have a key c.
str.size
.times
.with_object(Hash.new { |h,k| h[k] = [] }) { |i,h| h[str[i]] << i }
str.each_char
.with_index
.group_by(&:first)
.transform_values { |a| a.flat_map(&:last) }
See Enumerable#group_by, Hash#transform_values (introduced in Ruby v2.5) and Enumerable#flat_map.
Note that
str.each_char
.with_index
.group_by(&:first)
#=> {"h"=>[["h", 0]], "e"=>[["e", 1]], "l"=>[["l", 2], ["l", 3]],
# "o"=>[["o", 4]]}
Another option you can use is zipping two enumerations together.
s = "hello"
s.chars.zip(0..s.size)
This yields: [["h", 0], ["e", 1], ["l", 2], ["l", 3], ["o", 4]]
I am new to Ruby and I am sure this can be refactored, but another alternative might be:
arr1 = "Hello".split(%r{\s*})
arr2 = []
for i in 0..arr1.size - 1
arr2 << i
end
o = arr1.zip(arr2)
a_h = []
o.each do |i|
a_h << Hash[*i]
end
p a_h.each_with_object({}) { |k, v| k.each { |kk,vv| (v[kk] ||= []) << vv } }
=> {"H"=>[0], "e"=>[1], "l"=>[2, 3], "o"=>[4]}
I have an array of names. Each letter gets a value of 1 to 26 for the alphabet. Then the letter is multiplied by its place in the list. I came up with the following algorithm:
score = 0
names.each_with_index do |name, index|
temp = 0
letters = name.to_s.scan(/(.)/)
letters.each do |letter|
temp += 1 if letter.to_s.match(/A/)
temp += 2 if letter.to_s.match(/B/)
temp += 3 if letter.to_s.match(/C/)
temp += 4 if letter.to_s.match(/D/)
temp += 5 if letter.to_s.match(/E/)
temp += 6 if letter.to_s.match(/F/)
temp += 7 if letter.to_s.match(/G/)
temp += 8 if letter.to_s.match(/H/)
temp += 9 if letter.to_s.match(/I/)
temp += 10 if letter.to_s.match(/J/)
temp += 11 if letter.to_s.match(/K/)
temp += 12 if letter.to_s.match(/L/)
temp += 13 if letter.to_s.match(/M/)
temp += 14 if letter.to_s.match(/N/)
temp += 15 if letter.to_s.match(/O/)
temp += 16 if letter.to_s.match(/P/)
temp += 17 if letter.to_s.match(/Q/)
temp += 18 if letter.to_s.match(/R/)
temp += 19 if letter.to_s.match(/S/)
temp += 20 if letter.to_s.match(/T/)
temp += 21 if letter.to_s.match(/U/)
temp += 22 if letter.to_s.match(/V/)
temp += 23 if letter.to_s.match(/W/)
temp += 24 if letter.to_s.match(/X/)
temp += 25 if letter.to_s.match(/Y/)
temp += 26 if letter.to_s.match(/Z/)
end
score += (index+1) * temp
end
puts score
This is quite slow code. I hope someone can explain me a better and faster way to accomplish this task.
This is how I would do it.
Assumptions
the only characters are upper- and lower-case letters and whitespace.
after converting all characters of each element of the array to lower case, you want the letter offsets from a for each word to be summed and then multiplied by the (base 1) position of the word in the array.
Code
def totals_by_name(names)
names.each.with_index(1).with_object({}) { |(name,i),tots|
tots[name] = i*(name.downcase.each_char.reduce(0) { |tot,c|
tot + c.ord - 'a'.ord + 1 }) }
end
def total(names)
totals_by_name(names).values.reduce(:+)
end
Example
names = %w{ Joanne Jackie Joe Jethro Jack Jill }
#=> ["Joanne", "Jackie", "Joe", "Jethro", "Jack", "Jill"]
total(names)
#=> 914
Explanation
For the method totals_by_name and the array names above:
e0 = names.each
#=> #<Enumerator: ["Joanne", "Jackie", "Joe", "Jethro", "Jack", "Jill"]:each>
We can see the values this enumerator will pass on by converting it to an array:
e0.to_a
#=> ["Joanne", "Jackie", "Joe", "Jethro", "Jack", "Jill"]
Continuing,
e1 = e0.with_index(1)
#=> #<Enumerator: #<Enumerator: ["Joanne", "Jackie", "Joe", "Jethro",
# "Jack", "Jill"]:each>:with_index(1)>
e1.to_a
#=> [["Joanne", 1], ["Jackie", 2], ["Joe", 3], ["Jethro", 4], ["Jack", 5], ["Jill", 6]]
e2 = e1.with_object({})
#=> #<Enumerator: #<Enumerator: #<Enumerator: ["Joanne", "Jackie", "Joe",
# "Jethro", "Jack", "Jill"]:each>:with_index(1)>:with_object({})>
e2.to_a
#=> [[["Joanne", 1], {}], [["Jackie", 2], {}], [["Joe", 3], {}],
# [["Jethro", 4], {}], [["Jack", 5], {}], [["Jill", 6], {}]]
We can think of e2 and e3 as compound enumerators.
Enumerator#each passes elements of e2 to the block and assigns values to the block variables. We can use Enumerator#next to see what happens:
(name,i),tots = e2.next
#=> [["Joanne", 1], {}]
name #=> "Joanne"
i #=> 1
tots #=> {}
The block calculation is:
e3 = name.downcase.each_char
#=> #<Enumerator: "joanne":each_char>
e3.to_a # have a look
#=> ["j", "o", "a", "n", "n", "e"]
e3.reduce(0) { |tot,c| tot + c.ord - 'a'.ord + 1 }
#=> 59
For
c = "j"
this block calculation is:
tot + c.ord - 'a'.ord + 1
#=> 0 + 106 - 97 + 1
#=> 10
Therefore:
tots[name] = i*(name.downcase.each_char.reduce(0) { |tot,c|
tot + c.ord - 'a'.ord + 1 })
#=> tots["Joanna"] = 1*(59)
tots
#=> {"Joanne"=>59}
Values for other names are calculated similarly. The method tots is straightforward.
I would most likely go with:
class String
def points
each_char.map.with_index do |char, index|
(char.downcase.ord - 96) * (index + 1)
end.inject(0, :+)
end
end
'Hello'.points #=> 177
How to merge array of hash based on the same keys in ruby?
example :
a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]
How to get result like this?
a = [{:a=>[1, 10]},{:b=>8},{:c=>[7, 2]}]
Try
a.flat_map(&:entries)
.group_by(&:first)
.map{|k,v| Hash[k, v.map(&:last)]}
Another alternative:
a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]
p a.each_with_object({}) { |h, o| h.each { |k,v| (o[k] ||= []) << v } }
# => {:a=>[1, 10], :b=>[8], :c=>[7, 2]}
It also works when the Hashes have multiple key/value combinations, e.g:
b = [{:a=>1, :b=>5, :x=>10},{:a=>10, :y=>2},{:b=>8},{:c=>7},{:c=>2}]
p b.each_with_object({}) { |h, o| h.each { |k,v| (o[k] ||= []) << v } }
# => {:a=>[1, 10], :b=>[5, 8], :x=>[10], :y=>[2], :c=>[7, 2]}
Minor addition to answer by Arie Shaw to match required answer:
a.flat_map(&:entries)
.group_by(&:first)
.map{|k,v| Hash[k, v.size.eql?(1) ? v.last.last : v.map(&:last) ]}
#=> [{:a=>[1, 10]}, {:b=>8}, {:c=>[7, 2]}]
I'd do :
a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]
merged_hash = a.each_with_object({}) do |item,hsh|
k,v = item.shift
hsh[k] = hsh.has_key?(k) ? [ *Array( v ), hsh[k] ] : v
end
merged_hash.map { |k,v| { k => v } }
# => [{:a=>[10, 1]}, {:b=>8}, {:c=>[2, 7]}]
update
A better taste :
a = [{:a=>1},{:a=>10},{:b=>8},{:c=>7},{:c=>2}]
merged_hash = a.each_with_object({}) do |item,hsh|
k,v = item.shift
(hsh[k] ||= []) << v
end
merged_hash.map { |k,v| { k => v } }
# => [{:a=>[10, 1]}, {:b=>8}, {:c=>[2, 7]}]
Eg
:a=[["hello", "world"], ["good", "lord"], ["hello", "lord"]]
I need to find and record the indexes of each word with respect to the super-array.
i.e
hello => 0,2
world => 0
lord => 1,2.
here's my shot ,but its very amateurish and lengthy.
all_tokens=tokens.flatten
all_tokens.each do|keyword|
tokens.each do|token_array|
if token_array.include?keyword
x << i
end
i=i+1
end
y[k] = x.clone
y=y.clear
end
Slight improvement (imho) on vava's solution:
tokens = [["hello", "world"], ["good", "lord"], ["hello", "lord"]]
tokens_hash = Hash.new{|h, k| h[k] = []}
tokens.each_with_index do |subarr, i|
subarr.each do |word|
tokens_hash[word] << i
end
end
ret = []
a.each_with_index {|x, i| if x.include?(keyword) then ret << i end }
a.each_with_index.inject({}){|acc,(elem,i)|
elem.each{|e|
acc[e] ||= []
acc[e] << i
}
acc
}
#=> {"hello"=>[0, 2], "world"=>[0], "good"=>[1], "lord"=>[1, 2]}
tokens = [["hello", "world"], ["good", "lord"], ["hello", "lord"]]
tokens_hash = Hash.new([])
tokens.each_with_index do |subarr, i|
subarr.each do |word|
tokens_hash[word] = tokens_hash[word] + [i]
end
end
p tokens_hash #=>{"good"=>[1], "world"=>[0], "lord"=>[1, 2], "hello"=>[0, 2]}
My solution will scan the whole structure just once.
Just for grins, a functional solution:
#!/usr/bin/ruby1.8
a = [["hello", "world"], ["good", "lord"], ["hello", "lord"]]
b = a.flatten.uniq.inject({}) do |hash, word|
hash.merge(word => a.each_with_index.collect do |list, i|
list.index(word) && i
end.compact)
end
p b # => {"world"=>[0], "good"=>[1], "lord"=>[1, 2], "hello"=>[0, 2]}
a=[["hello", "world"], ["good", "lord"], ["hello", "lord"]]
result = Hash.new{|k,v| k[v] = []}
a.each_with_index{|b,i| b.each{|c| result[c] << i} }
result
#=> {"good"=>[1], "world"=>[0], "lord"=>[1, 2], "hello"=>[0, 2]}