Hello I have been doing some research for sometime on this particular project I have been working on and I am at a loss. What I am looking to do is use information from a file and convert that to a hash using some of those components for my key. Within the file I have:1,Foo,20,Smith,40,John,55
An example of what I am looking for I am looking for an output like so {1 =>[Foo,20], 2 =>[Smith,40] 3 => [John,55]}
Here is what I got.
h = {}
people_file = File.open("people.txt") # I am only looking to read here.
until people_file.eof?
i = products_file.gets.chomp.split(",")
end
people_file.close
FName = 'test'
str = "1,Foo,20,Smith, 40,John,55"
File.write(FName, str)
#=> 26
base, *arr = File.read(FName).
split(/\s*,\s*/)
enum = (base.to_i).step
arr.each_slice(2).
with_object({}) {|pair,h| h[enum.next]=pair}
#=> {1=>["Foo", "20"], 2=>["Smith", "40"],
# 3=>["John", "55"]}
The steps are as follows.
s = File.read(FName)
#=> "1,Foo,20,Smith, 40,John,55"
base, *arr = s.split(/\s*,\s*/)
#=> ["1", "Foo", "20", "Smith", "40", "John", "55"]
base
#=> "1"
arr
#=> ["Foo", "20", "Smith", "40", "John", "55"]
a = base.to_i
#=> 1
I assume the keys are to be sequential integers beginning with a #=> 1.
enum = a.step
#=> (1.step)
enum.next
#=> 1
enum.next
#=> 2
enum.next
#=> 3
Continuing,
enum = a.step
b = arr.each_slice(2)
#=> #<Enumerator: ["Foo", "20", "Smith", "40", "John", "55"]:each_slice(2)>
Note I needed to redefine enum (or execute enum.rewind) to reinitialize it. We can see the elements that will be generated by this enumerator by converting it to an array.
b.to_a
#=> [["Foo", "20"], ["Smith", "40"], ["John", "55"]]
Continuing,
c = b.with_object({})
#=> #<Enumerator: #<Enumerator: ["Foo", "20", "Smith", "40", "John", "55"]
# :each_slice(2)>:with_object({})>
c.to_a
#=> [[["Foo", "20"], {}], [["Smith", "40"], {}], [["John", "55"], {}]]
The now-empty hashes will be constructed as calculations progress.
c.each {|pair,h| h[enum.next]=pair}
#=> {1=>["Foo", "20"], 2=>["Smith", "40"], 3=>["John", "55"]}
To see how the last step is performed, each initially directs the enumerator c to generate the first value, which it passes to the block. The block variables are assigned to that value, and the block calculation is performed.
enum = a.step
b = arr.each_slice(2)
c = b.with_object({})
pair, h = c.next
#=> [["Foo", "20"], {}]
pair
#=> ["Foo", "20"]
h #=> {}
h[enum.next]=pair
#=> ["Foo", "20"]
Now,
h#=> {1=>["Foo", "20"]}
The calculations are similar for the remaining two elements generated by the enumerator c.
See IO::write, IO::read, Numeric#step, Enumerable#each_slice, Enumerator#with_object, Enumerator#next and Enumerator#rewind. write and read respond to File because File is a subclass of IO (File.superclass #=> IO). split's argument, the regular expression, /\s*,\s*/, causes the string to be split on commas together with any spaces that surround the commas. Converting [["Foo", "20"], {}] to pair and h is a product of Array Decompostion.
Related
I'm trying to reverse a string without using the built-in reverse method to get something like this:
input: "hello, world"
output: "world hello,"
I've been able to reverse the string to "dlrow ,olleh" so the words are the in the order they should be, but I'm stuck on how to reverse the individual words.
Suppose
str = "Three blind mice"
Here are three ways you could obtain the desired result, "mice blind Three", without using the method Array#reverse.
Split string, add index, use Enumerable#sort_by to sort array by index, join words
str.split.each_with_index.sort_by { |_,i| -i }.map(&:first).join(' ')
The steps are as follows.
a = str.split
#=> ["Three", "blind", "mice"]
enum = a.each_with_index
#=> #<Enumerator: ["Three", "blind", "mice"]:each_with_index>
b = enum.sort_by { |_,i| -i }
#=> [["mice", 2], ["blind", 1], ["Three", 0]]
c = b.map(&:first)
#=> ["mice", "blind", "Three"]
c.join(' ')
#=> "mice blind Three"
We can see the elements that will be generated by enum and passed to sort_by by converting enum to an array:
enum.to_a
#=> [["Three", 0], ["blind", 1], ["mice", 2]]
A disadvantage of this method is that it sorts an array, which is a relatively expensive operation. The next two approaches do not share that weakness.
Split string, use Array#values_at to extract words by index, highest to lowest join words
arr = str.split
arr.values_at(*(arr.size-1).downto(0).to_a).join(' ')
The steps are as follows.
arr = str.split
#=> ["Three", "blind", "mice"]
a = arr.size-1
#=> 2
b = a.downto(0).to_a
#=> [2, 1, 0]
c = arr.values_at(*b)
#=> ["mice", "blind", "Three"]
c.join(' ')
#=> "mice blind Three"
Use String#gsub to create an enumerator, chain to Enumerator#with_object, build string
str.gsub(/\w+/).with_object('') { |word,s|
s.prepend(s.empty? ? word : word + ' ') }
The steps are as follows.
enum1 = str.gsub(/\w+/)
#=> #<Enumerator: "Three blind mice":gsub(/\w+/)>
enum2 = enum1.with_object('')
#=> #<Enumerator: #<Enumerator: "Three blind mice":
# gsub(/\w+/)>:with_object("")>
enum2.each { |word,s| s.prepend(s.empty? ? word : word + ' ') }
#=> "mice blind Three"
When String#gsub is called on str without a block it returns an enumerator (see doc). The enumerator generates, and passes to with_object, matches of its argument, /\w+/; that is, words. At this point gsub no longer performs character replacement. When called without a block it is convenient to think of gsub as being named each_match. We can see the values that enum1 generates by converting it to an array (or execute Enumerable#entries on enum1):
enum1.to_a
#=> ["Three", "blind", "mice"]
Though Ruby has no such concept, it may be helpful to think of enum2 as a compound enumerator (study the return value for enum2 = enum1.with_object('') above). It will generate the following values, which is will pass to Enumerator#each:
enum2.to_a
#=> [["Three", ""], ["blind", ""], ["mice", ""]]
The second value of each of these elements is the initial value of the string that will be built and returned by each.
Let's now look at the first element element being generated by enum2 and passed to the block:
word, s = enum2.next
#=> ["Three", ""]
This first step is called Array decomposition.
word
#=> "Three"
s #=> ""
The block calculation is then as follows.
s.empty?
#=> true
t = word
#=> "Three"
s.prepend(t)
#=> "Three"
s #=> "Three"
Now the second element is generated by enum2 and passed to the block
word, s = enum2.next
#=> ["blind", "Three"]
word
#=> "blind"
s #=> "Three"
s.empty?
#=> false
t = word + ' '
#=> "blind "
s.prepend(t)
#=> "blind Three"
Notice that the value of second element of the array returned by enum2.next, the current value of s, has been updated to "Three".
The processing of the third and final element generated by enum2 (["mice", "blind Three"]) is similar, resulting in the block returning the value of s, "mice blind Three".
I have a hash:
hash = {"a_1_a" => "1", "a_1_b" => "2", "a_1_c" => "3", "a_2_a" => "3",
"a_2_b" => "4", "a_2_c" => "4"}
What's the best way to get the following sub-hashes:
[{"a_1_a" => "1", "a_1_b" => "2", "a_1_c" => "3"},
{"a_2_a" => "3", "a_2_b" => "4", "a_2_c" => "4"}]
I want them grouped by the key, based on the regexp /^a_(\d+)/. I'll have 50+ key/value pairs in the original hash, so something dynamic would work best, if anyone has any suggestions.
If you're only concerned about the middle component you can use group_by to get you most of the way there:
hash.group_by do |k,v|
k.split('_')[1]
end.values.map do |list|
Hash[list]
end
# => [{"a_1_a"=>"1", "a_1_b"=>"2", "a_1_c"=>"3"}, {"a_2_a"=>"3", "a_2_b"=>"4", "a_2_c"=>"4"}]
The final step is extracting the grouped lists and combining those back into the required hashes.
Code
def partition_hash(hash)
hash.each_with_object({}) do |(k,v), h|
key = k[/(?<=_).+(?=_)/]
h[key] = (h[key] || {}).merge(k=>v)
end.values
end
Example
hash = {"a_1_a"=>"1", "a_1_b"=>"2", "a_1_c"=>"3", "a_2_a"=>"3", "a_2_b"=>"4", "a_2_c"=>"4"}
partition_hash(hash)
#=> [{"a_1_a"=>"1", "a_1_b"=>"2", "a_1_c"=>"3"},
# {"a_2_a"=>"3", "a_2_b"=>"4", "a_2_c"=>"4"}]
Explanation
The steps are as follows.
enum = hash.each_with_object({})
#=> #<Enumerator: {"a_1_a"=>"1", "a_1_b"=>"2", "a_1_c"=>"3", "a_2_a"=>"3",
# "a_2_b"=>"4", "a_2_c"=>"4"}:each_with_object({})>
The first element of this enumerator is generated and passed to the block, and the block variables are computed using parallel assignment.
(k,v), h = enum.next
#=> [["a_1_a", "1"], {}]
k #=> "a_1_a"
v #=> "1"
h #=> {}
and the block calculation is performed.
key = k[/(?<=_).+(?=_)/]
#=> "1"
h[key] = (h[key] || {}).merge(k=>v)
#=> h["1"] = (h["1"] || {}).merge("a_1_a"=>"1")
#=> h["1"] = (nil || {}).merge("a_1_a"=>"1")
#=> h["1"] = {}.merge("a_1_a"=>"1")
#=> h["1"] = {"a_1_a"=>"1"}
so now
h #=> {"1"=>{"a_1_a"=>"1"}}
The next value of enum is now generated and passed to the block, and the following calculations are performed.
(k,v), h = enum.next
#=> [["a_1_b", "2"], {"1"=>{"a_1_a"=>"1"}}]
k #=> "a_1_b"
v #=> "2"
h #=> {"1"=>{"a_1_a"=>"1"}}
key = k[/(?<=_).+(?=_)/]
#=> "1"
h[key] = (h[key] || {}).merge(k=>v)
#=> h["1"] = (h["1"] || {}).merge("a_1_b"=>"2")
#=> h["1"] = ({"a_1_a"=>"1"}} || {}).merge("a_1_b"=>"2")
#=> h["1"] = {"a_1_a"=>"1"}}.merge("a_1_b"=>"2")
#=> h["1"] = {"a_1_a"=>"1", "a_1_b"=>"2"}
After the remaining four elements of enum have been passed to the block the following has is returned.
h #=> {"1"=>{"a_1_a"=>"1", "a_1_b"=>"2", "a_1_c"=>"3"},
# "2"=>{"a_2_a"=>"3", "a_2_b"=>"4", "a_2_c"=>"4"}}
The final step is simply to extract the values.
h.values
#=> [{"a_1_a"=>"1", "a_1_b"=>"2", "a_1_c"=>"3"},
# {"a_2_a"=>"3", "a_2_b"=>"4", "a_2_c"=>"4"}]
I have a set of array like this:
[["1","2"],["1","3"],["2","3"],["2","5"]]
I want to find the union of first values like
["1","2"],["1","3"] matches so i need to create new array like ["1","2,3"]
so the resulting array will be like
[["1","2,3"],["2","3,5"]]
Like most problems in Ruby, the Enumerable module does the job:
input = [["1","2"],["1","3"],["2","3"],["2","5"]]
result = input.group_by do |item|
# Group by first element
item[0]
end.collect do |key, items|
# Compose into new format
[
key,
items.collect do |item|
item[1]
end.join(',')
]
end
puts result.inspect
# => [["1", "2,3"], ["2", "3,5"]]
The group_by method comes in very handy when aggregating things like this, and collect is great for rewriting how the elements appear.
What you are asking for is not a true union for a true union of each 2 it would be:
data = [["1","2"],["1","3"],["2","3"],["2","5"]]
data.each_slice(2).map{|a,b| a | b.to_a }
#=> [["1","2","3"],["2","3","5"]]
Here is a very simple solution that modifies this concept to fit your needs:
data = [["1","2"],["1","3"],["2","3"],["2","5"]]
data.each_slice(2).map do |a,b|
unified = (a | b.to_a)
[unified.shift,unified.join(',')]
end
#=> [["1", "2,3"], ["2", "3,5"]]
Added to_a to piped variable b in the event that there are an uneven number of arrays. eg.
data = [["1","2"],["1","3"],["2","3"],["2","5"],["4","7"]]
data.each_slice(2).map do |a,b|
unified = (a | b.to_a)
[unified.shift,unified.join(',')]
end
#=> [["1", "2,3"], ["2", "3,5"], ["4","7"]]
If you meant that you want this to happen regardless of order then this will work but will destroy the data object
data.group_by(&:shift).map{|k,v| [k,v.flatten.join(',')]}
#=> [["1", "2,3"], ["2", "3,5"], ["4","7"]]
Non destructively you could call
data.map(&:dup).group_by(&:shift).map{|k,v| [k,v.flatten.join(',')]}
#=> [["1", "2,3"], ["2", "3,5"], ["4","7"]]
Here's another way.
Code
def doit(arr)
arr.each_with_object({}) { |(i,*rest),h| (h[i] ||= []).concat(rest) }
.map { |i,rest| [i, rest.join(',')] }
end
Examples
arr1 = [["1","2"],["1","3"],["2","3"],["2","5"]]
doit(arr1)
#=> [["1", "2,3"], ["2", "3,5"]]
arr2 = [["1","2","6"],["2","7"],["1","3"],["2","3","9","4","cat"]]
doit(arr2)
# => [["1", "2,6,3"], ["2", "7,3,9,4,cat"]]
Explanation
For arr1 above, we obtain:
enum = arr1.each_with_object({})
#=> #<Enumerator: [["1", "2"], ["1", "3"], ["2", "3"],
# ["2", "5"]]:each_with_object({})>
We can convert enum to an array see its elements:
enum.to_a
#=> [[["1", "2"], {}], [["1", "3"], {}],
# [["2", "3"], {}], [["2", "5"], {}]]
These elements will be passed into the block, and assigned to the block variables, by Enumerator#each, which will invoke Array#each. The first of these elements ([["1", "2"], {}]) can be obtained by invoking Enumerator#next on enum:
(i,*rest),h = enum.next
#=> [["1", "2"], {}]
i #=> "1"
rest #=> ["2"]
h #=> {}
We then execute:
(h[i] ||= []).concat(rest)
#=> (h["1"] ||= []).concat(["2"])
#=> (nil ||= []).concat(["2"])
#=> [].concat(["2"])
#=> ["2"]
each then passes the next element of enum to the block:
(i,*rest),h = enum.next
#=> [["1", "3"], {"1"=>["2"]}]
i #=> "1"
rest #=> ["3"]
h #=> {"1"=>["2"]}
(h[i] ||= []).concat(rest)
#=> (h["1"] ||= []).concat(["3"])
#=> (["2"] ||= []).concat(["3"])
#=> ["2"].concat(["3"])
#=> ["2", "3"]
After passing the last two elements of enum into the block, we obtain:
h=> {"1"=>["2", "3"], "2"=>["3", "5"]}
map creates an enumerator:
enum_h = h.each
#=> > #<Enumerator: {"1"=>["2", "3"]}:each>
and calls Enumerator#each (which calls Hash#each) to pass each element of enum_h into the block:
i, rest = enum_h.next
#=> ["1", ["2", "3"]]
then computes:
[i, rest.join(',')]
#=> ["1", ["2", "3"].join(',')]
#=> ["1", "2,3"]
The other element of enum_h is processed similarly.
Given input = "helloworld"
The output should be output = ["hello", "world"]
Given I have a method called is_in_dict? which returns true if there's a word given
So far i tried:
ar = []
input.split("").each do |f|
ar << f if is_in_dict? f
// here need to check given char
end
How to achieve it in Ruby?
Instead of splitting the input into characters, you have to inspect all combinations, i.e. "h", "he", "hel", ... "helloworld", "e", "el" , "ell", ... "elloworld" and so on.
Something like this should work:
(0..input.size).to_a.combination(2).each do |a, b|
word = input[a...b]
ar << word if is_in_dict?(word)
end
#=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ar
#=> ["hello", "world"]
Or, using each_with_object, which returns the array:
(0..input.size).to_a.combination(2).each_with_object([]) do |(a, b), array|
word = input[a...b]
array << word if is_in_dict?(word)
end
#=> ["hello", "world"]
Another approach is to build a custom Enumerator:
class String
def each_combination
return to_enum(:each_combination) unless block_given?
(0..size).to_a.combination(2).each do |a, b|
yield self[a...b]
end
end
end
String#each_combination yields all combinations (instead of just the indices):
input.each_combination.to_a
#=> ["h", "he", "hel", "hell", "hello", "hellow", "hellowo", "hellowor", "helloworl", "helloworld", "e", "el", "ell", "ello", "ellow", "ellowo", "ellowor", "elloworl", "elloworld", "l", "ll", "llo", "llow", "llowo", "llowor", "lloworl", "lloworld", "l", "lo", "low", "lowo", "lowor", "loworl", "loworld", "o", "ow", "owo", "owor", "oworl", "oworld", "w", "wo", "wor", "worl", "world", "o", "or", "orl", "orld", "r", "rl", "rld", "l", "ld", "d"]
It can be used with select to easily filter specific words:
input.each_combination.select { |word| is_in_dict?(word) }
#=> ["hello", "world"]
This seems to be a task for recursion. In short you want to take letters one by one until you get a word which is in dictionary. This however will not guarantee that the result is correct, as the remaining letters may not form a words ('hell' + 'oworld'?). This is what I would do:
def split_words(string)
return [[]] if string == ''
chars = string.chars
word = ''
(1..string.length).map do
word += chars.shift
next unless is_in_dict?(word)
other_splits = split_words(chars.join)
next if other_splits.empty?
other_splits.map {|split| [word] + split }
end.compact.inject([], :+)
end
split_words('helloworld') #=> [['hello', 'world']] No hell!
It will also give you all possible splits, so pages with urls like penisland can be avoided
split_words('penisland') #=> [['pen', 'island'], [<the_other_solution>]]
Simple ruby question. Lets say I have an array of 10 strings and I want to move elements at array[3] and array[5] into a totally new array. The new array would then only have the two elements I moved from the first array, AND the first array would then only have 8 elements since two of them have been moved out.
Use Array#slice! to remove the elements from the first array, and append them to the second array with Array#<<:
arr1 = ['Foo', 'Bar', 'Baz', 'Qux']
arr2 = []
arr2 << arr1.slice!(1)
arr2 << arr1.slice!(2)
puts arr1.inspect
puts arr2.inspect
Output:
["Foo", "Baz"]
["Bar", "Qux"]
Depending on your exact situation, you may find other methods on array to be even more useful, such as Enumerable#partition:
arr = ['Foo', 'Bar', 'Baz', 'Qux']
starts_with_b, does_not_start_with_b = arr.partition{|word| word[0] == 'B'}
puts starts_with_b.inspect
puts does_not_start_with_b.inspect
Output:
["Bar", "Baz"]
["Foo", "Qux"]
a = (0..9).map { |i| "el##{i}" }
x = [3, 5].sort_by { |i| -i }.map { |i| a.delete_at(i) }
puts x.inspect
# => ["el#5", "el#3"]
puts a.inspect
# => ["el#0", "el#1", "el#2", "el#4", "el#6", "el#7", "el#8", "el#9"]
As noted in comments, there is some magic to make indices stay in place. This can be avoided by first getting all the desired elements using a.values_at(*indices), then deleting them as above.
Code:
arr = ["null","one","two","three","four","five","six","seven","eight","nine"]
p "Array: #{arr}"
third_el = arr.delete_at(3)
fifth_el = arr.delete_at(4)
first_arr = arr
p "First array: #{first_arr}"
concat_el = third_el + "," + fifth_el
second_arr = concat_el.split(",")
p "Second array: #{second_arr}"
Output:
c:\temp>C:\case.rb
"Array: [\"null\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"s
even\", \"eight\", \"nine\"]"
"First array: [\"null\", \"one\", \"two\", \"four\", \"six\", \"seven\", \"eight
\", \"nine\"]"
"Second array: [\"three\", \"five\"]"
Why not start deleting from the highest index.
arr = ['Foo', 'Bar', 'Baz', 'Qux']
index_array = [2, 1]
new_ary = index_array.map { |index| arr.delete_at(index) }
new_ary # => ["Baz", "Bar"]
arr # => ["Foo", "Qux"]
Here's one way:
vals = arr.values_at *pulls
arr = arr.values_at *([*(0...arr.size)] - pulls)
Try it.
arr = %w[Now is the time for all Rubyists to code]
pulls = [3,5]
vals = arr.values_at *pulls
#=> ["time", "all"]
arr = arr.values_at *([*(0...arr.size)] - pulls)
#=> ["Now", "is", "the", "for", "Rubyists", "to", "code"]
arr = %w[Now is the time for all Rubyists to code]
pulls = [5,3]
vals = arr.values_at *pulls
#=> ["all", "time"]
arr = arr.values_at *([*(0...arr.size)] - pulls)
#=> ["Now", "is", "the", "for", "Rubyists", "to", "code"]