ruby string array iteration. Array of arrays - ruby

I have a ruby problem
Here's what i'm trying to do
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.each do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
#sum2
I know i have over complicated this
Basically the input is a string of letters and values like "14556 this word 398"
I am trying to sum the numbers in each value, seperated by the whitespace like (" ")
When i use the def iterate1 method the block calls the counter1 method just fine, but i can only get the value for the last word or value in the string.
In this case that's 398, which when summed would be 27.
If i include a break i get the first value, which would be 21.
I'm looking to output an array with all of the summed values
Any help would be greatly appreciated

I think you're after:
"10 d 20 c".scan(/\b\d+\b/).map(&:to_i).inject(:+) # Returns 30
scan(/\b\d+\b/) will extract all numbers that are made up of digits only in an array, map(&:to_i) will convert them to integers and I guess you already know what inject(:+) will do.
I'm not sure if I understand what you're after correctly, though, so it might help if you provide the answer you expect to this input.
EDIT:
If you want to sum the digits in each number, you can do it with:
"12 d 34 c".scan(/\b\d+\b/).map { |x| x.chars.map(&:to_i).inject(:+) }
x.chars will return an enumerator for the digits, map(&:to_i) will convert them to integers and inject(:+) will sum them.

The simplest answer is to use map instead of each because the former collects the results and returns an array. e.g:
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.map do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
end
You could write it a lot cleaner though and I think Stefan was a big help. You could solve the issue with a little modification of his code
# when you call iterate, you should pass in the value
# even if you have an instance variable available (e.g. #var2)
def iterate(thing)
thing.scan(/\b\d+\b/).map do |x|
x.chars.map{|d| counter1(d)}.inject(:+)
end
end
The above assumes that the counter1 method returns back the value as an integer

Related

Inserting variables into arrays

I want to generate text and insert them into an array. Please help.
new_vrms = Array.new[3] {"placeholder"}
puts "How many registration marks do you require?"
how_many = gets.chomp!()
i = 0
while i < how_many.to_i do
prefix =('a'..'z').to_a.shuffle.first(2).join
year = 68
suffix =('a'..'z').to_a.shuffle.first(3).join
aVRM = (prefix.to_s + year.to_s + suffix.to_s)
aVRM = aVRM.upcase!
puts ("#{aVRM} added to index #{i}")
#new_vrms.insert(0, 1) <-Array.rb:14:in `<main>': undefined method `insert' for nil:NilClass (NoMethodError)
#new_vrms.push << #aVRM <-Array.rb:15:in `<main>': undefined method `push' for nil:NilClass (NoMethodError)
#new_vrms[i] = ("#{aVRM}") <- Array.rb:16:in `<main>': undefined method `[]=' for nil:NilClass (NoMethodError)
i += 1
end
puts ("Succesfully generated "+ i.to_s + " registration marks")
The error is in the array initialization. What you have there (Array.new[3]) is seen by ruby as
(Array.new)[3]
You want to pass 3 to new as an argument.
Array.new(3)
This is an extended comment about the OPs code in general, so no upvotes please (downvotes are OK).
You begin with
new_vrms = Array.new[3] {"placeholder"}
#Sergio has identified your problem here, but beyond that there is no need to initialize the value of each element of the array ("placeholder") or even fix the size of the array. Indeed, you evidently wish to return the array with how_many elements and how_many is not yet know. Therefore, you should simply create an empty array here:
new_vrms = Array.new(0)
which is the same as
new_vrms = Array.new
which is more commonly written
new_vrms = []
Next you ask the user how many elements should be in the array:
how_many = gets.chomp!()
If the user enters "3", gets will return "3\n" and gets.chomp will return "3". Notice there is no need to end a method (here chomp) with () when it has no arguments. Also, chomp! is not incorrect but the non-mutating version, chomp, is generally used. You want how_many to be an integer, however, not a string (gets always returns a string). You therefore need to convert it to an integer:
how_many = gets.chomp.to_i
If you look at the doc for String#to_i, however, you will see that "123abc".to_i #=> 123, so "123\n".to_i #=> 123, meaning that we don't need chomp when converting a string to an integer:
how_any = gets.to_i
We now know the number of times we wish to repeat the loop (how_many), so you will want to use an iterator and block rather than a while loop:
how_many.times do |i|
...<your code to add elements to new_vrms>
end
new_vrms # return the computed value
See Integer#times.
You are repeating code in calculating prefix and suffix, so let's make that a separate method:
LETTERS = ('A'..'Z').to_a
#=> ["A", "B", "C",...,"X", "Y", "Z"]
def random_3_letters
LETTERS.sample(3).join
end
See Array#sample, which provides a more direct way of computing the random triples of letters. (Also, we may draw from an array of uppercase letters so we don't need to convert the samples to uppercase later.1 I created the constant LETTERS so that we don't need to create the array each time the method is called. We can now write the block.
YEAR = "68"
how_many.times do |i|
prefix = random_3_letters
suffix = random_3_letters
aVRM = prefix + YEAR + SUFFIX
puts "#{aVRM} added to index #{i}")
new_vrms.push(aVRM)
end
new_vrms
There is, in fact, no reason to define the variables prefix and suffix, as we can simplify as follows.
YEAR = "68"
how_many.times do |i|
aVRM = random_3_letters + YEAR + random_3_letters
puts "#{aVRM} added to index #{i}")
new_vrms.push(aVRM)
end
new_vrms
If you wish to print the value of each element of aVRM it's best to do that from outside the loop--more generally from outside a method you will wrap around the code. If the statement puts "#{aVRM} added to index #{i}") is extracted from the block the block variable i is no longer used, so it can be omitted:
YEAR = "68"
def doit
new_vrms = []
gets.to_i.times do
new_vrms << random_3_letters + YEAR + random_3_letters
end
new_vrms
end
Notice that I've changed Array#push to the more-commonly used Array#<< and substituted out the variables how_many and aVRS.
An experienced Rubyist might tighten this up even more (though input values would also be checked for validity in real code):
LETTERS = ('A'..'Z').to_a
YEAR = "68"
def doit
gets.to_i.times.with_object([]) do |_,new_vrms|
new_vrms << (random_3_letters + YEAR + random_3_letters)
end
end
def random_3_letters
LETTERS.sample(3).join
end
doit # entering "4"
#=> ["ZPI68LWY", "PQV68HLD", "IZG68JCH", "EAC68WLY"]
Notice that by using Enumerator#with_object we eliminate the statement new_vrms = [] and new_vrms at the end, the latter because with_object causes the block to return the "object", the value of new_vrms.
1 Note that you should never write str = str.upcase! because str.upcase! returns nil if str is already upcased (and therefore no changes are made to str). See String#upcase!. Many "bang" methods ...! return nil when no change is made to the receiver.

Bug in my Ruby counter

It is only counting once for each word. I want it to tell me how many times each word appears.
dictionary = ["to","do","to","do","to","do"]
string = "just do it to"
def machine(word,list)
initialize = Hash.new
swerve = word.downcase.split(" ")
list.each do |i|
counter = 0
swerve.each do |j|
if i.include? j
counter += 1
end
end
initialize[i]=counter
end
return initialize
end
machine(string,dictionary)
I assume that, for each word in string, you wish to determine the number of instances of that word in dictionary. If so, the first step is to create a counting hash.
dict_hash = dictionary.each_with_object(Hash.new(0)) { |word,h| h[word] += 1 }
#=> {"to"=>3, "do"=>3}
(I will explain this code later.)
Now split string on whitespace and create a hash whose keys are the words in string and whose values are the numbers of times that the value of word appears in dictionary.
string.split.each_with_object({}) { |word,h| h[word] = dict_hash.fetch(word, 0) }
#=> {"just"=>0, "do"=>3, "it"=>0, "to"=>3}
This of course assumes that each word in string is unique. If not, depending on the desired behavior, one possibility would be to use another counting hash.
string = "to just do it to"
string.split.each_with_object(Hash.new(0)) { |word,h|
h[word] += dict_hash.fetch(word, 0) }
#=> {"to"=>6, "just"=>0, "do"=>3, "it"=>0}
Now let me explain some of the constructs above.
I created two hashes with the form of the class method Hash::new that takes a parameter equal to the desired default value, which here is zero. What that means is that if
h = Hash.new(0)
and h does not have a key equal to the value word, then h[word] will return h's default value (and the hash h will not be changed). After creating the first hash that way, I wrote h[word] += 1. Ruby expands that to
h[word] = h[word] + 1
before she does any further processing. The first word in string that is passed to the block is "to" (which is assigned to the block variable word). Since the hash h is is initially empty (has no keys), h[word] on the right side of the above equality returns the default value of zero, giving us
h["to"] = h["to"] + 1
#=> = 0 + 1 => 1
Later, when word again equals "to" the default value is not used because h now has a key "to".
h["to"] = h["to"] + 1
#=> = 1 + 1 => 2
I used the well-worn method Enumerable#each_with_object. To a newbie this might seem complex. It isn't. The line
dict_hash = dictionary.each_with_object(Hash.new(0)) { |word,h| h[word] += 1 }
is effectively1 the same as the following.
h = Hash.new(0)
dict_hash = dictionary.each { |word| h[word] += 1 }
h
In other words, the method allows one to write a single line that creates, constructs and returns the hash, rather than three lines that do the same.
Notice that I used the method Hash#fetch for retrieving values from the hash:
dict_hash.fetch(word, 0)
fetch's second argument (here 0) is returned if dict_hash does not have a key equal to the value of word. By contrast, dict_hash[word] returns nil in that case.
1 The reason for "effectively" is that when using each_with_object, the variable h's scope is confined to the block, which is generally a good programming practice. Don't worry if you haven't learned about "scope" yet.
You can actually do this using Array#count rather easily:
def machine(word,list)
word.downcase.split(' ').collect do |w|
# for every word in `word`, count how many appearances in `list`
[w, list.count { |l| l.include?(w) }]
end.to_h
end
machine("just do it to", ["to","do","to","do","to","do"]) # => {"just"=>0, "do"=>3, "it"=>0, "to"=>3}
I think this is what you're looking for, but it seems like you're approaching this backwards
Convert your string "string" into an array, remove duplicate values and iterate through each element, counting the number of matches in your array "dictionary". The enumerable method :count is useful here.
A good data structure to output here would be a hash, where we store the unique words in our string "string" as keys and the number of occurrences of these words in array "dictionary" as the values. Hashes allow one to store more information about the data in a collection than an array or string, so this fits here.
dictionary = [ "to","do","to","do","to","do" ]
string = "just do it to"
def group_by_matches( match_str, list_of_words )
## trim leading and trailing whitespace and split string into array of words, remove duplicates.
to_match = match_str.strip.split.uniq
groupings = {}
## for each element in array of words, count the amount of times it appears *exactly* in the list of words array.
## store that in the groupings hash
to_match.each do | word |
groupings[ word ] = list_of_words.count( word )
end
groupings
end
group_by_matches( string, dictionary ) #=> {"just"=>0, "do"=>3, "it"=>0, "to"=>3}
On a side note, you should consider using more descriptive variable and method names to help yourself and others follow what's going on.
This also seems like you have it backwards. Typically, you'd want to use the array to count the number of occurrences in the string. This seems to more closely fit a real-world application where you'd examine a sentence/string of data for matches from a list of predefined words.
Arrays are also useful because they're flexible collections of data, easily iterated through and mutated with enumerable methods. To work with the words in our string, as you can see, it's easiest to immediately convert it to an array of words.
There are many alternatives. If you wanted to shorten the method, you could replace the more verbose each loop with an each_with_object call or a map call which will return a new object rather than the original object like each. In the case of using map.to_h, be careful as to_h will work on a two-dimensional array [["key1", "val1"], ["key2", "val2"]] but not on a single dimensional array.
## each_with_object
def group_by_matches( match_str, list_of_words )
to_match = match_str.strip.split.uniq
to_match.
each_with_object( {} ) { | word, groupings | groupings[ word ] = list_of_words.count( word ) }
end
## map
def group_by_matches( match_str, list_of_words )
to_match = match_str.strip.split.uniq
to_match.
map { | word | [ word, list_of_words.count( word ) ] }.to_h
end
Gauge your method preferences depending on performance, readability, and reliability.
list.each do |i|
counter = 0
swerve.each do |j|
if i.include? j
counter += 1
needs to be changed to
swerve.each do |i|
counter = 0
list.each do |j|
if i.include? j
counter += 1
Your code is telling how many times each word in the word/string (the word which is included in the dictionary) appears.
If you want to tell how many times each word in the dictionary appears, you can switch the list.each and swerve.each loops. Then, it will return a hash # => {"just"=>0, "do"=>3, "it"=>0, "to"=>3}

Iterate over an array of characters in steps

I have an array of hex characters that is > 8000 characters, and I need to do some operation on every 6 characters in the array.
Ranges in ruby have a really cool step feature:
(1..100).step(6) do //etc....
Is there any kind of functionality similar to this for arrays?
Something like:
string.split("").step(6) do //etc...
You want Enumerable#each_slice:
require 'enumerator' # if pre-Ruby1.9
string.split("").each_slice(6) do |ary|
# ary is a 6-length array, and this is executed for every block of 6 characters
end
You say that you have an array of characters, but then you show code using string.split("").
More efficient than using split("")—which will create an intermediary array of 8,000 strings before beginning, wasting both time and memory—use the String#chars enumerator along with each_slice:
string.chars.each_slice(6) do |a,b,c,d,e,f|
# one variable for each character
end
or
string.chars.each_slice(6) do |a|
# a is an array of all six
end
(Also note each_cons(6), in case that's what you really meant.)
Use Array.each_slice(6):
%w[a b c d e f g h i j k l m n o p].each_slice(6) { |s| puts s.join(',') }
a,b,c,d,e,f
g,h,i,j,k,l
m,n,o,p
You could just use a range with step, and Array's method values_at. This method takes one or more integers, or a range as indices argument. Unfortunately Range's step method doesn't return a range, but an enumerable. This can however simply be converted to an array of integers by prefixing an asterisk *.
string = "You could just use a range with step, and Array's method values_at."
step6 = (0...string.length).step(6)
string.split("").values_at(*step6).each do |char|
puts char
end

Storing output into a variable to be used in an array

A snippet of my code below flips a coin and outputs a result of 10 total heads or tails.
(e.g. Heads Tails Heads Tails...)
I'd like to store this into a variable where I can put it into an array and use its strings.
%w[act] only outputs the string "act". How can I get that line of code to output my array of strings from the line act = coin.flip?
Updated and added full code
class Coin
def flip
flip = 1 + rand(2)
if flip == 2
then puts "Heads"
else
puts "Tails"
end
end
end
array = []
10.times do
coin = Coin.new
array << coin.flip
end
puts array
This:
10.times do
coin = Coin.new
act = coin.flip
end
doesn't produce an array. It simply creates ten coin flips and throws them all away, the result of that expression is, in fact, 10. If you want an array, you'll need to build one.
You could take Douglas's approach or try something a bit more idiomatic.
The Integer#times method returns an enumerator so you can use any of the Enumerable methods on it rather than directly handing it a block. In particular, you could use collect to build an array in one nice short piece of code:
a = 10.times.collect { Coin.new.flip }
That gives you 10 flips in the Array a and then you can puts a or puts a.join(', ') or whatever you want.
The %w[] won't work because that's for generating an Array of whitespace separated words:
%w[] Non-interpolated Array of words, separated by whitespace
So %w[a b c] is just a nicer way of saying ['a', 'b', 'c'] and the words within %w[] are treated as single quoted strings rather than variables or method calls to be evaluated.
Seems that there is some editing going on. You'll also want to modify your flip method to return the flip rather than print it:
def flip
flip = 1 + rand(2)
if flip == 2
"Heads"
else
"Tails"
end
end
Then you'll get your Heads and Rails in the array.
Put the act results into an array.
arr = []
10.times do
coin = Coin.new
arr << coin.flip
end
p arr # => [...]

Explaining a Ruby code snippet

I'm in that uncomfortable position again, where somebody has left me with a code snippet in a language I don't know and I have to maintain it. While I haven't introduced Ruby to myself some parts of it are quite simple, but I'd like to hear your explanations nonetheless.
Here goes:
words = File.open("lengths.txt") {|f| f.read }.split # read all lines of a file in 'words'?
values = Array.new(0)
words.each { |value| values << value.to_i } # looked this one up, it's supposed to convert to an array of integers, right?
values.sort!
values.uniq!
diffs = Array.new(0) # this looks unused, unless I'm missing something obvious
sum = 0
s = 0 # another unused variable
# this looks like it's computing the sum of differences between successive
# elements, but that sum also remains unused, or does it?
values.each_index { |index| if index.to_i < values.length-1 then sum += values.at(index.to_i + 1) - values.at(index.to_i) end } # could you also explain the syntax here?
puts "delta has the value of\n"
# this will eventually print the minimum of the original values divided by 2
puts values.at(0) / 2
The above script was supposed to figure out the average of the differences between every two successive elements (integers, essentially) in a list. Am I right in saying this is nowhere near what it actually does, or am I missing something fundamental, which is likely considering I have no Ruby knowledge?
Explanation + refactor (non used variables removed, functional approach, each_cons):
# Read integer numbers from file, sort them ASC and remove duplicates
values = File.read("lengths.txt").split.map(&:to_i).sort.uniq
# Take pairwise combinations and get the total sum of partial differences
partial_diffs = values.each_cons(2).map { |a, b| b - a }.inject(0, :+)
That guy surely didn't grasp Ruby himself. I wonder why he chose to use that language.
Here's an annotated explanation:
# Yes, it reads all lines of a file in words (an array)
words = File.open("lengths.txt") {|f| f.read }.split
values = Array.new(0)
# Yes, to_i convert string into integer
words.each { |value| values << value.to_i }
values.sort!
values.uniq!
# diffs and s seem unused
diffs = Array.new(0)
sum = 0
s = 0
# The immediate line below can be read as `for(int index = 0; index < values.length; index++)`
values.each_index { |index|
# index is integer, to_i is unnecessary
if index.to_i < values.length-1 then
# The `sum` variable is used here
# Following can be rewritten as sum += values[i-1] - values[i]
sum += values.at(index.to_i + 1) - values.at(index.to_i)
end
}
puts "delta has the value of\n"
# Yes, this will eventually print the minimal of the original values divided by 2
puts values.at(0) / 2
To help you get a better grasp of what "real" (idiomatic) Ruby looks like, I've written what you wanted, with some annotations
values = open("lengths.txt") do |f|
# Read it like this:
#
# Take the list of all lines in a file,
# apply a function to each line
# The function is stripping the line and turning it
# into an integer
# (This means the resultant list is a list of integers)
#
# And then sort it and unique the resultant list
#
# The eventual resultant list is assigned to `values`
# by being the return value of this "block"
f.lines.map { |l| l.strip.to_i }.sort.uniq
end
# Assign `diffs` to an empty array (instead of using Array.new())
diffs = []
values.each_index do |i|
# Syntactic sugar for `if`
# It applies the 1st part if the 2nd part is true
diffs << (values[i+1] - values[i]) if i < values.length - 1
end
# You can almost read it like this:
#
# Take the list `diffs`, put all the elements in a sentence, like this
# 10 20 30 40 50
#
# We want to inject the function `plus` in between every element,
# so it becomes
# 10 + 20 + 30 + 40 + 50
#
# The colon `:+` is used to refer to the function `plus` as a symbol
#
# Take the result of the above summation, divided by length,
# which gives us average
delta = diffs.inject(:+) / diffs.length
# `delta` should now contains the "average of differences" between
# the original `values`
# String formatting using the % operator
# No \n needed since `puts` already add one for us
puts "delta has the value of %d" % delta
That is by no means pushing the true power of Ruby, but you see why Rubyists get so enthusiastic about expressiveness and stuffs :P
values.each_index { |index| if index.to_i < values.length-1 then sum += values.at(index.to_i + 1) - values.at(index.to_i) end }
The above line sums the differences between consecutive values. the test index.to_i < values.length-1 is to not access the array out of bounds, because of values.at(index.to_i + 1).
You are right, this code does not do much thing. it only prints half of the minimum value from the file.

Resources