I am new to ruby and have this program that takes in a number of names and sorting them into pairs of two, and throwing the odd person in a random group. Sometimes it works perfect, sometimes it throws the extra person into an array of their own, and im not sure why. I know there is a cleaner way to do this but Im just trying to understand how the code works. For example it should return "Apple" "Banana" "Orange" as ["Banana", "Orange", "Apple"] and will most of the time, but sometimes it give me ["Banana","Orange",] ["Apple"] Any advice?
def randomArray
classNames = []
puts "Please enter a list of names to sort"
while true
input = gets.chomp
break if input.empty?
classNames << input
end
classRandom = classNames.shuffle
splitNames = classRandom.each_slice(2).to_a
arrayPos = 0
splitNames.length.times do
if splitNames[arrayPos].length == 2
arrayPos+=1
else splitNames[arrayPos].length == 1
splitNames.sample << splitNames[arrayPos].pop
arrayPos+=1
end
end
x = 0
splitNames.length.times do
break if splitNames[x].empty?
puts "Group number #{x+1} is #{splitNames[x]}"
x+=1
end
end
randomArray
Your problem is this: splitNames.sample << splitNames[arrayPos].pop
sample can return any element of the array, including the element that has the odd person you're trying to assign! So if it samples that person, it removes them from their group of 1 and then adds them right back in.
To fix it, take advantage of the fact that either all groups will be pairs, or the last group will have a single person. Don't iterate over the array, just check splitNames[-1]. If they are alone, add them to splitNames[0...-1].sample.
Related
I'm trying to create a replica of Go Fish to help me learn more about arrays and hashes, and just how to go about structuring data. I'm on day two and have what looks to be much closer to the end goal. Keep in mind, I'm new to this. Anyway, here's the problem I'm running into:
=> gofish.rb:21:in `player_turn': no implicit conversion of String into Integer (TypeError)
I understand why I'm getting the error, but I can't figure out how to use the .shift method without giving an index number. I would like to select which object to shift based on the value instead. So, if I correctly guess do you have an 'ace of spades', the card is removed from the cpu_hand array and is added to the my_hand array. With that said, I would just like to know the best way to go about this.
Here's my script:
card_values = ['ace', 'two', 'three', 'four', 'five', 'six', 'seven',
'eight', 'nine', 'ten', 'jack', 'queen', 'king']
suits = ['spades', 'diamonds', 'hearts', 'clubs']
# creates array objects with every value and suit possible (full deck)
card_deck = card_values.product(suits).collect{|card, suit| "#{card} of #{suit}"}
def print_hand
puts "Your hand: #{#my_hand.join(', ')}."
end
def player_turn
puts "You go first!"
puts "Do you have a..."
puts #cpu_hand.join(', ')
print "> "
#card = $stdin.gets.chomp.downcase
# if cpu has the card requested give it to the player and add to their array
if #cpu_hand.include?(#card)
puts "Ahhh...you got me. Here you go!"
#my_hand.shift(#cpu_hand[#card]) # ****Here's the error(line:21)
print_hand
else
puts "Go fish!"
#my_hand.shift(#card_deck[1])
print_hand
end
end
puts "There are #{card_deck.length} cards in this deck."
puts "Welcome to Go-Fish."
print "Your name please: "
player_name = $stdin.gets.chomp.capitalize
puts "Ok #{player_name}, lets get this deck shuffled..."
#sleep(1)
# shuffles card_deck using .shuffle method
card_deck = card_deck.shuffle
puts "Cards are perfectly shuffled!"
#sleep(1)
puts "Dealing cards..."
#sleep(1)
# assigns first 7 cards to user, removes from card_deck
#my_hand = Array.new
#my_hand = card_deck.shift(7)
# assigns next 7 cards to CPU, removes from card_deck
#cpu_hand = Array.new
#cpu_hand = card_deck.shift(7)
print_hand
until card_deck.length < 1 || #cpu_hand.length < 1 || #my_hand.length < 1
player_turn
end
puts "GAME OVER!"
In your error line,
#my_hand.shift(#cpu_hand[#card])
your crash is caused by attempting to index into the #cpu_hand array with a string (#card). The second issue is trying to call shift using a string as a parameter. Both of these need to be integers. shift removes the first element (or first n elements if an integer parameter is specified); this seems appropriate for taking an item from card_deck, but you'd want push, unshift, or << to add an element to #my_hand. You'll also need a method to remove the specified card from #cpu_hand, delete:
if #cpu_hand.include?(#card)
puts "Ahhh...you got me. Here you go!"
#my_hand << #cpu_hand.delete(#card) # transfer a card from CPU to my hand
print_hand
else
puts "Go fish!"
#my_hand << card_deck.shift # take the first card from the deck (could also be pop?)
print_hand
end
However, card_deck is not a global like your other variables, so you'll need to pass it into the function and change the function header to def player_turn card_deck, or make it global. But making everything global is generally considered poor design because data can be mutated from anywhere, leading to difficult-to-find bugs. Consider writing classes such as Hand and GoFish (for example) that encapsulate all of the logic necessary to represent reusable pieces of your game.
I recommend taking a read through the array docs. You'll surely find some cool methods and learn a few tidbits.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
While I thought it was going to be straightforward as doing a comparison of arrays it is not working as expected. They can only be compared with a regex pattern.
EXAMPLE of what the two arrays look like:
first array contains lone words like, house, cat, dog etc. The second string contains phrases "I have a house", "i have a cat". So the point is to see what words of first array are contained in the second array that has the phrases
SAMPLE ARRAY WORDS: {house, dog, cat, man, girl, etc}
SAMPLE ARRAY PHRASES: {"I have a house", "I am a dog", "I am man" etc}
I have gone through all the array methods in the documentation here
https://docs.ruby-lang.org/en/2.0.0/Array.html
Is not to use subtraction of arrays to see differences, we are not talking about numbers. It is not to check if some are included because the boolean is not an answer to me and besides,
The bad and strange result that I am getting is that it prints me out the full of the second array as if everything from the first array was contained there, which is not the case.
I have tried 3 variants, .each, select, and for.
Note: if instead of using a variable I hardcode a string that I know it is part of the second array, it gets me the results fine. Also, IF I write a variable and I assign it the value by hand such as this: item = house and they put it into the pattern of the regex, it also works! so the problem would seem to be that somehow the items are not being extracted from the first array, but that is not the case because I tried printing them and they print out fine. So, if they are correctly extracted from the first array, and they work if I assign by hand the value of the variable, why can't I just assign automatically the values to the variable as they come out of the first array? that is what drives me insane. 8 hours so far.
The code is not long, only 4 lines, I have included multiple approaches I have tried. 5 actually
TAKE NUMBER ONE:
words= Array.new
File.open('mtos-array.txt').each { |line| linesdb << line }
phrases = Array.new
File.open('out23.txt').each { |line| fcia << line }
$x = 0
for item in words do
for mto in phrases
if(mto =~/#{item}/)
puts"it is contained in #{mto}"
end
end
puts $x
$x +=1
end
TAKE NUMBER TWO
x = words.select {|num| num}
phrases.each {|a|
if(a =~/#{x}/)
puts"coincide com #{a}"
end
}
THIS THIRD TAKE very compact does not work either. It prints me out the full of the second array (linesdb). I would have wanted to print ONLY if x is contained in v
x = words.select {|num| num}
puts(phrases.select {|v| v =~ /#{x}/})
TAKE NUMBER 4
This harcoded value works, only that I need variables instead but insanely cant have them work. To make it even clearer: Suppose the word house comes from the first array, it indeed finds me all the phrases in the second array that contain the word "house".
num = "house"
phrases.each {|a| if(a =~/#{num}/)
puts"coincide en #{a}"
end
}
TAKE NUMBER 5
again it prints me out the full second array
for item in words do
phrases.each {|a| if(a =~/#{item}/)
puts"it is contained in en #{a}"
end
}
end
words = ["house", "dog", "cat", "man", "girl"]
phrases = ["I have a house", "I am a dog", "I am man"]
words.select { |word| phrases.any? { |phrase| phrase.include? word } }
#=> ["house", "dog", "man"]
I'm trying to write some code that will loop through an array of strings, clean up the entries, and then add the cleaned up entries to a hash that tracks the frequency with which each word appears. This was my first solution:
puts("Give me your text.")
text = gets.chomp
words = text.split
frequencies = Hash.new(0)
words.map! do |word|
word.tr("\",.", "")
end
words.each do |word|
frequencies[word] += 1
end
It works fine, but looping through the array twice feels very inefficient, so I've been trying to find a way to do it one go and stumbled upon the following:
puts("Give me your text.")
text = gets.chomp
words = text.split
frequencies = Hash.new(0)
words.each_with_index do |word, index|
words[index].tr!("\",.", "")
frequencies[word] += 1
end
Based on my understanding of each_with_index, this shouldn't work, but somehow it does, and the hash receives the clean version of each string: https://repl.it/B9Gw. What's going on here? And is there a different way to solve this problem without looping twice?
EDIT: After some reading, I was able to solve the problem using just one loop in the following way:
puts("Give me your text.")
text = gets.chomp
words = text.split
frequencies = Hash.new(0)
for i in 0..words.length-1
words[i].tr!("\",.", "")
frequencies[words[i]] += 1
end
However, this is more of a JS or C++ solution and doesn't seem like idiomatic Ruby. Are there any other options? Also, why does the each_with_index approach even work?
You are using the String#tr! method, which modifies the string destructively instead of returning a new string. The fact that you are looking it up on the hash again (using words[index]) doesn't change anything, because the string object is still the same - so the word you use to modify the frequencies hash is also modified.
And is there a different way to solve this problem without looping twice?
An obvious way would be to use the same logic that you used, but without the with_index (which isn't making any difference here anyway). I would advise using the non-destructive String#tr instead of String#tr!, to make it more clear which strings have been cleaned and which have not.
frequencies = Hash.new(0)
words.each do |word|
cleaned = word.tr("\",.", "")
frequencies[cleaned] += 1
end
If you want to make clear the map phase of the process and still only loop once, you can leverage ruby's lazy enumerators:
frequencies = Hash.new(0)
cleaned_words = words.lazy.map { |word| word.tr("\",.", "") }
cleaned_words.each do |cleaned|
frequencies[cleaned] += 1
end
Here, even though we do a map and then an each, the collection is only traversed once, and ruby doesn't create any intermediary arrays.
How does Python know what "i" is when it is not defined, shouldn't there be an error? Probably a simple explanation, but I am new to learning Python.
def doubles (sum):
return sum * 2
myNum = 2
for i in range (0,3):
myNum = doubles(myNum)
print (myNum)
Haha :-) People are marking down your question, but I know that is one question must have came in every person's mind. Specially those who learned Python through Online courses and not through a teacher in person.
Well let me explain that in layman's term,
The method that you used is specially used for 1) lists and 2) lists within lists.
For eg,
example1= ['a','b','c'] # This is a simple list
example2 = [['a','b','c'],['a','b','c'],['a','b','c']] # This is list within lists.
Now, 'a','b' & 'c' are items in list.
So by saying,
for i in example1:
print i
we are actually saying,
for item in the list(example1):
print item
-------------------------
People use 'i', probably taken as abbreviation to item, or something else.
I don't know the history.
But, the fact is that, we can use anything instead or 'i' and Python will still consider it as an item in list.
Let me give you examples again.
example1= ['a','b','c'] # This is a simple list
example2 = [['a','b','c'],['a','b','c'],['a','b','c']] # This is list within lists.
for i in example1:
print i
[out]: a
b
c
now in example2, items are lists within lists. --- also, now i will use the word 'item' instead of 'i' --- the results regardless would be the same for both.
for item in example2:
print item
[out]: ['a','b','c']
['a','b','c']
['a','b','c']
people also use singulars and plurals to remember things,
so lets we have a list of alphabet.
letters=['a','b','c','d']
for letter in letters:
print letter
[out]: a
b
c
d
Hope that helps. There is much more to explain.
Keep researching and keep learning.
Regards,
Md. Mohsin
Using a variable as a loop control variable does assign to it each time through the loop.
As to "what it is"... Python is dynamically typed. The only thing it "is" is a name, just like any other variable.
i is assigned the value in the loop itself, it has no value (it is not defined) before the Python interpreter reaches the for line.
Its similar to how other for loops define variables. In C++ for example:
for(int i=0; i<5; i++){
cout << i << endl;
}
Here the variable i is only exists once the for loop is called.
i is assigned a value when the for loop runs, so the Python interpreter will not raise an error when the loop is run
long story short it creates a new variable without having to be defined and its value is whatever number your loop is on, for example if you had written:
num = 0
for i in range(3):
print(num)
num = num + 1
so for the first time this loop ran 'i' would equal 0 (because python lists/loops etc always start on 0 not 1), the second time it would equal 1, etc. and the 'num' you can ignore it's just an example of code you could have in a loop which would print out numbers in ascending order.
Levin
as a green hand python programmer, I have a little problem, I'll appreciate if somebody can help!!
I have two lists
a list of random repeated numbers(unknow size) like:
number = [44,198,57,48,658,24,7,44,44,44..]
for n in number
I want to give these numbers to a list of people in order, one number for one person. If a number repeat, the program will find out the person whom got this number when the first time it shows up. It means I want to print a list like
people = [1,2,3,4,5,6,7,1,1,1...]
print people
Store the mapping from numbers to indexes in a dict, and update it as you go.
numbers = [44,198,57,48,658,24,7,44,44,44.]
index_mapping = {}
indexes = []
next_index = 1
for number in numbers:
if number in index_mapping:
# already seen
indexes.append(index_mapping[number])
else:
# a new one
indexes.append(next_index)
index_mapping[number] = next_index
next_index += 1
print indexes