why doesn't this logic work in the initialize method? - ruby

def initialize(letters)
#letters = letters
#face = letters.sample # letters is an array of all letters from A to Z
if #face == "Q"
#face = "Qu"
end
#visited = false
#coord = []
end
When I p my array of dice later, I see that the #face is still "Q"
..., [#<Dice:0x007f907b032948 #letters=["H", "I", "M", "N", "Q", "U"], #face="Q", #visited=false, #coord=[]>, ...
What's going on ?

the sample method selects a random element from the array. I think you'll find that if #letters were to equal ['Q'], your code will work.

Related

Ruby Anagrams puzzle

i a m a begginner in ruby please i need directions in how to get the program to return a list containing "inlets"
Question: Given a word and a list of possible anagrams, select the correct sublist.
Given "listen" and a list of candidates like "enlists" "google" "inlets" "banana" the program should return a list containing "inlets".
This is what i have been able to do
puts 'Enter word'
word_input = gets.chomp
puts 'Enter anagram_list'
potential_anagrams = gets.chomp
potential_anagrams.each do |anagram|
end
this is how the program should behave assuming my word_input was "hello" but i do not know how to get this working.
is_anagram? word: 'hello', anagrams: ['helo', 'elloh', 'heelo', 'llohe']
# => 'correct anagrams are: elloh, llohe'
Would really appreciate ideas.
As hinted in comments, a string can be easily converted to an array of its characters.
irb(main):005:0> "hello".chars
=> ["h", "e", "l", "l", "o"]
irb(main):006:0> "lleho".chars
=> ["l", "l", "e", "h", "o"]
Arrays can be easily sorted.
irb(main):007:0> ["h", "e", "l", "l", "o"].sort
=> ["e", "h", "l", "l", "o"]
irb(main):008:0> ["l", "l", "e", "h", "o"].sort
=> ["e", "h", "l", "l", "o"]
And arrays can be compared.
irb(main):009:0> ["e", "h", "l", "l", "o"] == ["e", "h", "l",
l", "o"]
=> true
Put all of this together and you should be able to determine if one word is an anagram of another. You can then pair this with #select to find the anagrams in an array. Something like:
def is_anagram?(word, words)
words.select do |w|
...
end
end
I took the tack of creating a class in pursuit of re-usability. This is overkill for a one-off usage, but allows you to build up a set of known words and then poll it as many times as you like for anagrams of multiple candidate words.
This solution is built on hashes and sets, using the sorted characters of a word as the index to a set of words sharing the same letters. Hashing is O(1), Sets are O(1), and if we view words as having a bounded length the calculation of the key is also O(1), yielding an overall complexity of a constant time per word.
I've commented the code, but if anything is unclear feel free to ask.
require 'set'
class AnagramChecker
def initialize
# A hash whose default value is an empty set object
#known_words = Hash.new { |h, k| h[k] = Set.new }
end
# Create a key value from a string by breaking it into individual
# characters, sorting, and rejoining, so all strings which are
# anagrams of each other will have the same key.
def key(word)
word.chars.sort.join
end
# Add individual words to the class by generating their key value
# and adding the word to the set. Using a set guarantees no
# duplicates of the words, since set contents are unique.
def add_word(word)
word_key = key(word)
#known_words[word_key] << word
# return the word's key to avoid duplicate work in find_anagrams
word_key
end
def find_anagrams(word)
word_key = add_word(word) # add the target word to the known_words
#known_words[word_key].to_a # return all anagramatic words as an array
end
def inspect
p #known_words
end
end
Producing a library of known words looks like this:
ac = AnagramChecker.new
["enlists", "google", "inlets", "banana"].each { |word| ac.add_word word }
ac.inspect # {"eilnsst"=>#<Set: {"enlists"}>, "eggloo"=>#<Set: {"google"}>, "eilnst"=>#<Set: {"inlets"}>, "aaabnn"=>#<Set: {"banana"}>}
Using it looks like:
p ac.find_anagrams("listen") # ["inlets", "listen"]
p ac.find_anagrams("google") # ["google"]
If you don't want the target word to be included in the output, adjust find_anagrams accordingly.
Here's how I would do it.
Method
def anagrams(list, word)
ltr_freq = word.each_char.tally
list.select { |w| w.size == word.size && w.each_char.tally == ltr_freq }
end
Example
list = ['helo', 'elloh', 'heelo', 'llohe']
anagrams(list, 'hello')
#=> ["elloh", "llohe"]
Computational complexity
For practical purposes, the computational complexity of computing w.each_char.tally for any word w of length n can be regarded as O(n). That's because hash key lookups are nearly constant-time. It follows that the computational complexity of determining whether a word of length n is an anagram of another word of the same length can be regarded as O(n).
This compares with methods that sort the letters of a word, which have a computational complexity of O(n*log(n)), n being the word length.
Explanation
See Enumerable#tally. Note that w.each_char.tally is not computed when w.size == word.size is false.
Now let's add some puts statements to see what is happening.
def anagrams(list, word)
ltr_freq = word.each_char.tally
puts "ltr_freq = #{ltr_freq}"
list.select do |w|
puts "\nw = '#{w}'"
if w.size != word.size
puts "words differ in length"
false
else
puts "w.each_char.tally = #{w.each_char.tally}"
if w.each_char.tally == ltr_freq
puts "character frequencies are the same"
true
else
puts "character frequencies differ"
false
end
end
end
end
anagrams(list, 'hello')
ltr_freq = {"h"=>1, "e"=>1, "l"=>2, "o"=>1}
w = 'helo'
words differ in length
w = 'elloh'
w.each_char.tally = {"e"=>1, "l"=>2, "o"=>1, "h"=>1}
character frequencies are the same
w = 'heelo'
w.each_char.tally = {"h"=>1, "e"=>2, "l"=>1, "o"=>1}
character frequencies differ
w = 'llohe'
w.each_char.tally = {"l"=>2, "o"=>1, "h"=>1, "e"=>1}
character frequencies are the same
#=>["elloh", "llohe"]
Possible improvement
A potential weakness of the expression
w.each_char.tally == ltr_freq
is that the frequencies of all unique letters in the word w must be determined before a conclusion is reached, even if, for example, the first letter of w does not appear in the word word. We can remedy that as follows.
def anagrams(list, word)
ltr_freq = word.each_char.tally
list.select do |w|
next false unless w.size == word.size
ltr_freqs_match?(w, ltr_freq.dup)
end
end
def ltr_freqs_match?(w, ltr_freq)
w.each_char do |c|
return false unless ltr_freq.key?(c)
ltr_freq[c] -= 1
ltr_freq.delete(c) if ltr_freq[c].zero?
end
true
end
One would have to test this variant against the original version of anagrams above to determine which tends to be fastest. This variant has the advantage that it terminates (short-circuits) the comparison as soon as is found that the cumulative count of a given character in the word w is greater than the total count of the same letter in word. At the same time, tally is written in C so it may still be faster.

Ruby permutations

Simply put, I want to have an input of letters, and output all possible combinations for a set length range.
for example:
length range 1 - 2
input a, b, c
...
output a, b, c, aa, ab, ac, bb, ba, bc, cc, ca, cb
I am trying to make an anagram/spell check solver so that I can 'automate' the NYT's Spelling Bee game. So, I want to input the letters given into my program, get an array of all possible combinations for specific lengths (they have a min word length of 4) and then check that array against an array of all English words. What I have so far is:
letters = ["m","o","r"]
words = []
# Puts all the words into an array
File.open('en_words.txt') do |word|
word.each_line.each do |line|
words << line.strip
end
end
class String
def permutation(&block)
arr = split(//)
arr.permutation { |i| yield i.join }
end
end
letters.join.permutation do |i|
p "#{i}" if words.include?(i)
end
=>"mor"
=>"rom"
my issue with the above code is that it stop
s at the number of letters I have given it. For example, it will not repeat to return "room" or "moor". So, what I am trying to do is get a more complete list of combinations, and then check those against my word list.
Thank you for your help.
How about going the other way? Checking every word to make sure it only uses the allowed letters?
I tried this with the 3000 most common words and it worked plenty fast.
words = [..]
letters = [ "m", "o", "r" ]
words.each do |word|
all_letters_valid = true
word.chars.each do |char|
unless letters.include?(char)
all_letters_valid = false
break
end
end
if all_letters_valid
puts word
end
end
If letters can repeat there isn't a finite number of permutations so that approach doesn't make sense.
Assumption: English ascii characters only
If the goal is not to recode the combination for an educational purpose :
In the ruby standard library, the Array class has a combination method.
Here an examples :
letters = ["m","o","r"]
letters.combination(2).to_a
# => [["m", "o"], ["m", "r"], ["o", "r"]]
You also have a magic permutation method :
letters.permutation(3).to_a
# => [["m", "o", "r"], ["m", "r", "o"], ["o", "m", "r"], ["o", "r", "m"], ["r", "m", "o"], ["r", "o", "m"]]
If the goal is to recode theses methods. Maybe you can use them as validation. For exemple by counting the elements in your method and in the standard library method.

Count array with condition

So I have a array of characters and I'd like to display all permutations of a given size meeting a certain condition. For instance, if my array contains 'L', 'E' and 'A' and I choose to display all permutations of size 3 that ends with 'L'. There are two possibilities, ["A", "E", "L"] and ["E", "A", "L"].
My problem is: how can I count the number of possibilities and print all the possibilities within the same each? Here's what I have so far:
count = 0
combination_array.select do |item|
count += 1 if item.last == 'L'
puts "#{item} " if item.last == 'L'
end
It works fine, but I have to write the condition 2 times and also I can't write before displaying all possibilities. I've created a method
def count_occurrences(arr)
counter = 0
arr.each do |item|
counter += 1 if item.last == 'L'
end
counter
end
but I still have to repeat my condition (item.last == 'L'). it doesn't seem very efficient to me.
You could use each_cons (docs) to iterate through each set of 3 items, and count (docs) in block form to have Ruby count for you without constructing a new array:
matches = [["E", "A", "L"], ["A", "E", "L"]]
match_count = data.each_cons(3).count do |set|
if matches.include?(set)
puts set.to_s
return true
end
end
If you really dislike the conditional block, you could technically simplify to a one-liner:
stuff_from_above.count do |set|
matches.include?(set) && !(puts set.to_s)
end
This takes advantage of the fact that puts always evaluates to nil.
And if you're feeling extra lazy, you can also write ["A", "E", "L"] as %w[A E L] or "AEL".chars.
If you specifically want to display and count permutations that end in "L", and the array arr is known to contain exactly one "L", the most efficient method is to simply generate permutations of the array with "L" removed and then tack "L" onto each permutation:
arr = ['B', 'L', 'E', 'A']
str_at_end = 'L'
ar = arr - [str_at_end]
#=> ["B", "E", "A"]
ar.permutation(2).reduce(0) do |count,a|
p a + [str_at_end]
count += 1
end
#=> 6
displaying:
["B", "E", "L"]
["B", "A", "L"]
["E", "B", "L"]
["E", "A", "L"]
["A", "B", "L"]
["A", "E", "L"]
If you want to do something else as well you need to state specifically what that is.
Note that the number of permutations of the elements of an array of size n is simply n! (n factorial), so if you only need the number of permutations with L at the end you could compute that as factorial(arr.size-1), where factorial is a simple method you would need to write.

Replacing every letter in a given string with the letter following in the alphabet [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have to replace every letter in a string with the letter following it in the alphabet (i.e. c becomes d, z becomes a), capitalize every vowel (a, e, i, o, u), and return the modified string. I'm trying to find solutions without calling any functions like sort or find.
I have this:
def LetterChanges(str)
Changed_Letter = ""
alphabet = [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]
for i in 0..str.length do
if str[i] ==
str[i] = alphabet[i] + 1
return str
end
but I am lost. Any help is appreciated.
You are being asked to "map" each letter of the alphabet to another letter, so you will want to use the method Enumerable#map.
VOWELS = "aeiou"
letters = ('a'..'z').to_a
#=> ["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"]
letters.map do |c|
<code referencing c>
end
#=> ['b', 'c', 'd', 'E', 'f',..., 'z', 'A]
Now let's fill in the code, using the methods:
String#succ, which, given a character, returns the character with the next-higher ASCII value. For example, "b".ord #=> 98, so "b".succ #=> "c", since "c".ord #=> 99. Since "z".succ #=> 'aa', we need to treat "z" as a special case. String#succ is the same as String#next.
String#include?, which, given a string, returns true or false depending on whether include?'s argument (a string) is included in the receiver. For example, "cat".include?("at") #=> true; "cat".include?("a") #=> true; "cat".include?("z") #=> false. Note that VOWELS, since it begins with a capital letter, is a constant.
String#upcase, which converts all lowercase letters in a given string to upper case (and leaves all other characters unchanged).
letters.map do |c|
if c == 'z'
'A'
else
s = c.succ
if VOWELS.include?(s)
s.upcase
else
s
end
end
end
#=> ["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", "A"]
You could instead write this using a case statement and Ruby's ternary operator:
letters.map do |c|
case c
when 'z'
'A'
else
s = c.succ
VOWELS.include?(s) ? s.upcase : s
end
end
or you could make use of the methods String#ord and Integer#chr:
letters.map do |c|
s = ('a'.ord + ((c.ord-'a'.ord+1) % 26)).chr
VOWELS.include?(s) ? s.upcase : s
end
end
If, for example, c = 'r'
('a'.ord + ((c.ord-'a'.ord+1) % 26).chr
#=> (97 + ((114-97+1) % 26).chr
#=> (97 + 18 % 26).chr
#=> (97 + 18).chr
#=> 115.chr
#=> 's'
If, however, c = 'z'
('a'.ord + ((c.ord-'a'.ord+1) % 26).chr
#=> (97 + ((122-97+1) % 26).chr
#=> (97 + 26 % 26).chr
#=> (97 + 0).chr
#=> 97.chr
#=> 'a'
One more way. (You can figure out why this works.)
letters.map do |c|
s = c.succ[0]
VOWELS.include?(s) ? s.upcase : s
end
You might instead wish to create a hash.
letter_mapping = {}
letters.each do |c|
s = c.succ[0]
letter_mapping[c] = VOWELS.include?(s) ? s.upcase : s
end
letter_mapping
#=> { "a"=>"b", "b"=>"c", "c"=>"d", "d"=>"E", "e"=>"f", "f"=>"g", "g"=>"h",
# "h"=>"I", "i"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"O",
# "o"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"U", "u"=>"v",
# "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"A"}
so, for example, letter_mapping['r'] #=> "s".
In time you will find that the Ruby way of writing this is:
letters.each_with_object({}) do |c, letter_mapping|
s = c.succ[0]
letter_mapping[c] = VOWELS.include?(s) ? s.upcase : s
end
#=> { "a"=>"b", ... "z"=>"A"}
One last thing. Enumerable#map is an instance method for every class that includes the Enumerable module. One such class is Array:
Array.included_modules
#=> [Enumerable, Kernel]
Array.instance_methods.include?(:map)
#=> true
Array has use of all of the module Enumerable's methods, just as though they had been defined in Array. That's why map works when the receiver is an array.
Another class that includes Enumerable is Range:
Range.included_modules
#=> [Enumerable, Kernel]
Range.instance_methods.include?(:map)
#=> true
Therefore, instead of writing:
letters = ('a'..'z').to_a
we could (should) write:
letters = ('a'..'z')
and all the above code would work just fine.
You can try this, it will replace a letter with its following letter also it will capitalize vowels.
def letter_changes(str)
alphabets = ('a'..'z').to_a
vowels = ["a","e","i","o","u"]
for i in 0..(str.length-1) do
index = (alphabets.index(str[i]) == (alphabets.size - 1) ? 0 : (alphabets.index(str[i]) + 1))
str[i] = alphabets[index]
str[i] = str[i].upcase if vowels.include?(str[i])
end
puts str
end
## call function
letter_changes("cadcarz")
## OUTPUT
dbEdbsA

function that returns the number of letters that repeat in a string

Trying to make a function that counts the number of letters that appear more than once anywhere in a string (not necessarily together, and not the the number of times they repeat). This is what I have:
def num_repeats(string)
repeat = []
i1 = 0
i2 = 1
while i1 < string.length
while i2 < string.length
if (string[i1] == string[i2]) && (!repeat.include? string[i1])
repeat << string[i1]
end
i2 +=1
end
i1+=1
end
return repeat.length
end
puts(num_repeats('sldhelanlaskjkajksda'))
For some reason, it only pushes the first letter of the string if that first letter has been used in the rest of the string, but after that, it seems like the method stops looping through the rest of the string.
I'd like to know first why the current code is not working and if there is a way to fix it, and I also welcome other better solutions.
Here is an orthodox way to do it:
'sldhelanlaskjkajksda'.each_char.group_by(&:itself).count{|_, v| v.length > 1}
# => 6
The reason your code does not work is because, (i) once the i2 loop terminates, you increment i1, and try for another i2 loop in the next i1 iteration, but since i2 hasn't been touched after failed to satisfy the loop condition, it will not satisfy the condition again, and the i2 loop will never run again, and (ii) you are initializing i2 to a constant.
To fix it, initialize i2 within i1 loop at the beginning, and initialize it to i2 = i1 + 1, not 1.
Another way:
s = 'sldhelanlaskjkajksda'
a = s.chars
#=> ["s", "l", "d", "h", "e", "l", "a", "n", "l", "a",
# "s", "k", "j", "k", "a", "j", "k", "s", "d", "a"]
a.difference(a.uniq).uniq.size
#=> 6
where Array#difference is defined in my answer here.
We have:
b = a.uniq
#=> ["s", "l", "d", "h", "e", "a", "n", "k", "j"]
c = a.difference(b)
#=> ["l", "l", "a", "s", "k", "a", "j", "k", "s", "d", "a"]
d = c.uniq
#=> ["l", "a", "s", "k", "j", "d"]
d.size
#=> 6
None of these answers consider that OP asked for repeating letters
But this does:
'sldhe-lanlas-kjkajksda'.scan(/([a-z])(?=.*\1)/i).uniq.size
#=> 6
This is the solution for your problem
def num_repeats(string)
repeat = []
i1 = 0
i2 = 1
while i1 < string.length
while i2 < string.length
if (string[i1] == string[i2]) && !(repeat.include? string[i1])
repeat << string[i1]
end
i2 +=1
end
i1+=1
i2 = i1 + 1
end
return repeat.length
end
puts(num_repeats('sldhelanlaskjkajksda'))
Here is bit simpler (hopefully) and little Ruby-ish, solution:
def num_repeats(string)
# chars in string
chars = string.split('')
# initialize map - for each char, count is initialized to 0
hash = chars.uniq.inject({}) { |h, c| h[c] = 0; h}
# for each char in string, lets count its occurrences
chars.each do |c|
hash[c] += 1
end
# now lets pick those entries from the map where the count is > 1
hash_with_repeated_chars = hash.select {|k, v| v > 1 }
# now lets pick the chars that are repeated by picking keys of hash
repeated_chars = hash_with_repeated_chars.select { |k, v| k}
# return the count of repeated chars
return repeated_chars.count
end
p num_repeats('abc') # Prints 0
p num_repeats('abbc') # Prints 1
p num_repeats('abbcc') # Prints 2
p num_repeats('aabbcc') # Prints 3
I also have a Ruby version that is different from all other answers (and hence, bit inefficient due to multiple iterations it does internally)
s = 'sldhelanlaskjkajksda'
p s.chars.combination(2).to_a.uniq.map(&:sort).map(&:uniq).select{|a| a.size.eql?(1)}.count
Create a hash and set the default value to 0. Use the gsub method to remove all white space from the string. Convert the string into an array of string characters using the split method. Iterate over each letter in the array and store the key as each letter and the number of times each letter occurs as the key's value. Finally Remove any key from the hash with a value that is less than 2. Return the hash's length since this corresponds to the number of letters in the hash that have occurred more than once in your original string. Hope this explanation helps and that it answers your question. The code below could be more compact but it is in its current form in the hopes of being more explanatory and informative.
def counter(string)
counts = Hash.new(0)
result = string.gsub(" ","")
result = result.split('')
result.each do |letter|
counts[letter] += 1
end
counts.delete_if { |key,value| value < 2}
return counts.length
end

Resources