Related
I was taking a quiz in Ruby that required me to find the total number of unique combinations between flavors and toppings but I was stuck on a particular part. One of the rules was that "chocolate chip ice cream can't have chocolate chips toppings" or some wording similar to that. This wasn't the exact problem but I tried my best to create a similar problem. How would I go about solving this problem?
def combinations(flavors, toppings)
end
flavors = ["fudge", "vanilla", "chocolate chip", "cookie dough"] # 11
toppings = ["chocolate chips", "sprinkles", "mint"]
I first was thinking of doing a nested loop problem but that part that I was stuck on is that "chocolate chip" and "chocolate chips" isn't the same.
Consider an analogous situation. Use #product to find all combinations and then #reject to eliminate the ones that fail to meet the criteria.
In the below example, the second string cannot contain the first one. #index will return nil is the substring is not found.
x = ["a", "b", "c"]
y = ["as", "hello", "world"]
x.product(y).reject { |a, b| b.index(a) }
# => [["a", "hello"], ["a", "world"], ["b", "as"],
# ["b", "hello"], ["b", "world"], ["c", "as"],
# ["c", "hello"], ["c", "world"]]
I don't want to give it away completely if it is a quiz question, but as always when solving programming problems, a good start is to break it down into smaller steps. I think the following should give you a good hint on how to do it.
Define what it means for two flavours to be the same. Is "chocolate chip" the same as "chocolate chips"? (note the 's' that is different) What about "chocolate" ice cream with "chocolate chips"? One way would be to say that two flavours are equal if one is a substring of the other. Another, slightly harder, way would be to say that they must be exactly the same, normalising away plural forms. Write yourself a function that can take two flavours and give you a true/false answer.
Hint: Check out the docs for the #include? method on String if you go with the first definition and remember that you might not know in advance which string is a substring of the other.
Build a list of all possible pairs when taking one element from each of the two arrays.
Hint: Check out the #product method on Array as mentioned by #jvx8ss.
Remove all pairs where both elements of the pair are equal according to the definition from step 1.
Hint: Check out the #reject method on Array.
All of the relevant docs can be found at https://ruby-doc.org/3.1.3/
Building off of Chris's answer and considering not only that the first string should not be contained in the second, but also the other way around. I think it might be slightly better to sort the array first before doing the check with reject.
def combinations(flavors, toppings)
flavors.product(toppings).reject do |combo|
first, second = combo.sort
second.include?(first)
end.count
end
flavors = ["fudge", "vanilla", "chocolate chip", "cookie dough"]
toppings = ["chocolate chips", "sprinkles", "mint"]
pp combinations(flavors, toppings) # => 11
I have been reading this:
https://docs.ruby-lang.org/en/2.4.0/Enumerator.html
I am trying to understand why someone would use .to_enum, I mean how is that different than just an array? I see :scan was passed into it, but what other arguments can you pass into it?
Why not just use .scan in the case below? Any advice on how to understand .to_enum better?
"Hello, world!".scan(/\w+/) #=> ["Hello", "world"]
"Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"]
"Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]
Arrays are, necessarily, constructs that are in memory. An array with a a lot of entries takes up a lot of memory.
To put this in context, here's an example, finding all the "palindromic" numbers between 1 and 1,000,000:
# Create a large array of the numbers to search through
numbers = (1..1000000).to_a
# Filter to find palindromes
numbers.select do |i|
is = i.to_s
is == is.reverse
end
Even though there's only 1998 such numbers, the entire array of a million needs to be created, then sifted through, then kept around until garbage collected.
An enumerator doesn't necessarily take up any memory at all, not in a consequential way. This is way more efficient:
# Uses an enumerator instead
numbers = (1..1000000).to_enum
# Filtering code looks identical, but behaves differently
numbers.select do |i|
is = i.to_s
is == is.reverse
end
You can even take this a step further by making a custom Enumerator:
palindromes = Enumerator.new do |y|
1000000.times do |i|
is = (i + 1).to_s
y << i if (is == is.reverse)
end
end
This one doesn't even bother with filtering, it just emits only palindromic numbers.
Enumerators can also do other things like be infinite in length, whereas arrays are necessarily finite. An infinite enumerator can be useful when you want to filter and take the first N matching entries, like in this case:
# Open-ended range, new in Ruby 2.6. Don't call .to_a on this!
numbers = (1..).to_enum
numbers.lazy.select do |i|
is = i.to_s
is == is.reverse
end.take(1000).to_a
Using .lazy here means it does the select, then filters through take with each entry until the take method is happy. If you remove the lazy it will try and evaluate each stage of this to completion, which on an infinite enumerator never happens.
I am currently using this function, and the code works exactly as it should.
self.chars.permutation.map(&:join).uniq.group_by(&:chr)
However, once the string is more than 10 characters, it takes a lot of time to generate all permutations. How could I generate permutations quicker?
Rather than computing all permutations of each word, a better approach is to first create a hash from the dictionary, whose keys are strings sorted by character and whose values are arrays containing all words in the dictionary which are anagrams of the key. The arrays are empty when the word contains no anagrams in the dictionary (other than itself).
words = %w| god act bat tar a lion stop |
#=> ["god", "act", "bat", "tar", "a", "lion", "stop"]
dictionary = %w| cat dog a fowl bat god act lion pig donkey loin post pots
spot stop tops|
#=> ["cat", "dog", "a", "fowl", "bat", "god", "act", "lion", "pig",
# "donkey", "loin", "post", "pots", "spot", "stop", "tops"]
h = dictionary.each_with_object(Hash.new { |h,k| h[k] = [] }) do |w,h|
h[w.each_char.sort.join] << w
end
#=> {"act"=>["cat", "act"], "dgo"=>["dog", "god"], "a"=>["a"], "flow"=>["fowl"],
# "abt"=>["bat"], "ilno"=>["lion", "loin"], "gip"=>["pig"], "deknoy"=>["donkey"],
# "opst"=>["post", "pots", "spot", "stop", "tops"]}
We can then obtain all the anagrams of each word in words by sorting the word on its characters and seeing whether that is a key in the hash.
words.each_with_object({}) do |w,g|
key = w.downcase.chars.sort.join
values = h.key?(key) ? (h[key]-[w]) : []
g[w] = values
end
#=> {"god"=>["dog"], "act"=>["cat"], "bat"=>[], "tar"=>[], "a"=>[],
# "lion"=>["loin"], "stop"=>["post", "pots", "spot", "tops"]}
I am currently using this function, and the code works exactly as it should.
self.chars.permutation.map(&:join).uniq.group_by(&:chr)
However, once the string is more than 10 characters, it takes a lot of time to generate all permutations. How could I generate permutations quicker?
You can't. Well, maybe there are ways of speeding it up a little, but there is really no point: the number of permutations is much too large. For just 25 characters, even if we assume that you can generate one permutation for every CPU cycle, even if we assume that you have a 5GHz CPU, even if we assume that your CPU has 100 cores, even if we assume that the work can be perfectly distributed among those cores, it will still take close to one million years to generate. There's just that many of them.
In short: there is no point in even trying to speed up your algorithm. You need to get away from generating permutations altogether.
Theory
No need for permutations :
Sort the letters in your string
Sort the letters in every word in the dictionary
Look for same sorted letters
Done!
Implementation
class String
def sorted_letters
downcase.chars.sort.join
end
end
class AnagramFinder
#dict = '/usr/share/dict/american-english'
class << self
def get_anagrams(word)
sorted_dict[word.sorted_letters]
end
def all
sorted_dict.values.select { |anagrams| anagrams.size > 1 }
end
def sorted_dict
#sorted_dict ||= generate_sorted_dict
end
private
def generate_sorted_dict
File.foreach(#dict).with_object(Hash.new { |h, k| h[k] = [] }) do |word, sorted_dict|
word.chomp!
sorted_dict[word.sorted_letters] << word
end
end
end
end
p AnagramFinder.get_anagrams('impressiveness')
#=> ["impressiveness", "permissiveness"]
p AnagramFinder.get_anagrams('castor')
#=> ["Castor", "Castro", "Croats", "actors", "castor", "costar", "scrota"]
p AnagramFinder.all.last(5)
#=> [["wist", "wits"], ["withers", "writhes"], ["woodworm", "wormwood"], ["wriest", "writes"], ["wrist", "writs"]]
p AnagramFinder.all.max_by(&:length)
#=> ["Stael", "Tesla", "least", "slate", "stale", "steal", "tales", "teals"]
This example needed 0.5s on my slowish server, and most of it was spent building the sorted dictionary. Once it is done, the lookup is almost instantaneous.
"impressiveness" has 14 characters, you would need a very long time to generate all the permutations (14! = 87178291200).
Perhaps lazy might be an option. It doesn't need as much memory as generation all permutations before checking for a special condition.
Something like:
'my_string'.chars.permutation.lazy.map(&:join).each do |permutation|
puts permutation if dictionary.include?(permutation)
end
If we look at Permutation we see the number of permutations of an eleven letter word with no letter repeated would be 39,916,800. However for MISSISSIPPI it is 11! / ( 1! * 4! * 4! * 2!) = 34,650. The first is going to take a long time however you do it, but if you can reduce the search space using repeating characters it might become more manageable. The standard permutation method does not remove repeats.
Searching for "ruby permutations without repetition" might turn up some algorithms.
So I'm writing what I thought was a simple .rb file to convert a float number into a string. The string returns my floating point number in words. So if I have 11.11 then I would have eleven dollars and eleven cents So far I've extended the float class which has worked alright. I'm having trouble with how to convert the 11 cents into eleven cents. en.numwords would kick back eleven point one one. I've thought about trying out a hash to solve my problem where 11=>eleven cents. Any thoughts how I could implement this? Perhaps a better way to implement this?
Here's what I have so far:
require 'rubygems'
require 'linguistics'
Linguistics::use( :en )
class Float
def to_test_string
puts self #check
puts self.en.numwords
self.en.numwords
end
end
puts "Enter two great floating point numbers for adding"
puts "First number"
c = gets.to_f
puts "Second number"
d = gets.to_f
e = c+d
# puts e
puts e.to_test_string
puts "Enter a great floating number! Example 10.34"
a = gets.to_f
# puts a
puts a.to_test_string
Thanks for the help! Post some code so I can try ideas out!
Here's one solution: divide the number into two substrings based on the decimal point delimiter, call en.numwords on each substring separately, and then join the resulting strings with "point" between them. Something along the lines of:
require "rubygems"
require "linguistics"
Linguistics::use(:en)
class Float
def my_numwords
self.to_s.split('.').collect { |n| n.en.numwords }.join(' point ')
end
end
(11.11).my_numwords # => eleven point eleven
This problem can be solved by splitting the float into two values: dollars and cents.
require 'rubygems'
require 'linguistics'
Linguistics::use( :en )
class Float
def to_test_string
puts self #check
#Split into dollars and cents
cents = self % 1
dollars = self - cents
cents = cents * 100
text = "#{dollars.to_i.en.numwords} dollars and #{cents.to_i.en.numwords} cents"
puts text
text
end
end
puts "Enter two great floating point numbers for adding"
puts "First number"
c = gets.to_f
puts "Second number"
d = gets.to_f
e = c+d
# puts e
puts e.to_test_string
puts "Enter a great floating number! Example 10.34"
a = gets.to_f
# puts a
puts a.to_test_string
I want to use a for-each and a counter:
i=0
for blah in blahs
puts i.to_s + " " + blah
i+=1
end
Is there a better way to do it?
Note: I don't know if blahs is an array or a hash, but having to do blahs[i] wouldn't make it much sexier. Also I'd like to know how to write i++ in Ruby.
Technically, Matt's and Squeegy's answer came in first, but I'm giving best answer to paradoja so spread around the points a bit on SO. Also his answer had the note about versions, which is still relevant (as long as my Ubuntu 8.04 is using Ruby 1.8.6).
Should've used puts "#{i} #{blah}" which is a lot more succinct.
As people have said, you can use
each_with_index
but if you want indices with an iterator different to "each" (for example, if you want to map with an index or something like that) you can concatenate enumerators with the each_with_index method, or simply use with_index:
blahs.each_with_index.map { |blah, index| something(blah, index)}
blahs.map.with_index { |blah, index| something(blah, index) }
This is something you can do from ruby 1.8.7 and 1.9.
[:a, :b, :c].each_with_index do |item, i|
puts "index: #{i}, item: #{item}"
end
You can't do this with for. I usually like the more declarative call to each personally anyway. Partly because its easy to transition to other forms when you hits the limit of the for syntax.
Yes, it's collection.each to do loops, and then each_with_index to get the index.
You probably ought to read a Ruby book because this is fundamental Ruby and if you don't know it, you're going to be in big trouble (try: http://poignantguide.net/ruby/).
Taken from the Ruby source code:
hash = Hash.new
%w(cat dog wombat).each_with_index {|item, index|
hash[item] = index
}
hash #=> {"cat"=>0, "wombat"=>2, "dog"=>1}
If you don't have the new version of each_with_index, you can use the zip method to pair indexes with elements:
blahs = %w{one two three four five}
puts (1..blahs.length).zip(blahs).map{|pair|'%s %s' % pair}
which produces:
1 one
2 two
3 three
4 four
5 five
As to your question about doing i++, well, you cannot do that in Ruby. The i += 1 statement you had is exactly how you're supposed to do it.
If you want to get index of ruby for each, then you can use
.each_with_index
Here is an example to show how .each_with_index works:
range = ('a'..'z').to_a
length = range.length - 1
range.each_with_index do |letter, index|
print letter + " "
if index == length
puts "You are at last item"
end
end
This will print:
a b c d e f g h i j k l m n o p q r s t u v w x y z You are at last item
The enumerating enumerable series is pretty nice.
If blahs is a class that mixes in Enumerable, you should be able to do this:
blahs.each_with_index do |blah, i|
puts("#{i} #{blah}")
end