class String
def mgsub(key_value_pairs=[].freeze)
regexp_fragments = key_value_pairs.collect { |k,v| k }
gsub(Regexp.union(*regexp_fragments)) do |match|
key_value_pairs.detect{|k,v| k =~ match}[1]
end
end
end
puts "GO HOME!".mgsub([[/.*GO/i, 'HoMe'], [/home/i, 'is where the heart is']])
puts "Here is number #123".mgsub([[/[a-z]/i, '#'], [/#/, 'P']])
key_value_pairs is an array of arrays, each a pair. detect takes out one element, which is an array (pair). [1] takes out the latter (second) value of it.
Related
I am currently working on a basic Ruby programming project, that focuses on creating classes, and operations on those classes. I have very little experience, but understand the general idea of Ruby.
My task is making an Array2 class. Creating arrays from the class, perform operations on the arrays. The methods I attempted are a to-string method, and a is-reverse method that has two array parameters, and tests if the first array is the reverse of the second array.
Here is my attempt, I tried but I am having trouble passing the arrays properly into the class. Also I believe that I am having some calling complications.
class Array2
def initialize (a)
#array = Array.new(a)
end
def to_s
return #array
end
def isreverse (array1,array2)
reverasea = Array.new
reverasea = array1.reverse
if (reversea = array2) then
return "The First Array Is The Reverse Of The Second Array"
else
return "The First Array Is Not The Reverse Of The Second Array"
end
end
end
array1 = ["4","5","6","7"]
array2 = ["7","6","5","3"]
a1 = Array2.new(array1)
a2 = Array2.new(array2)
puts a1.to_s
puts a2.to_s
puts a1.isreverse(array1, array2)
You have an assignment where you probably meant equality test:
if (reversea = array2) then
you could dispense with reversea entirely and just test (this requires a reverse method in Array2)
if (array1.reverse == #array) then
I would personally make isreverse a boolean, and eliminate the need to pass in the same array again:
def isreverse? (array1)
return (#array.reverse == array1)
end
then use it like
puts "The First Array Is#{a1.isreverse?(a2)?"":" Not"} The Reverse Of The Second Array"
put it all together and it looks like:
class Array2
def initialize (a)
#array = Array.new(a)
end
def to_s
return #array
end
def reverse
#array.reverse
end
def isreverse? (array1)
return (array1.reverse == #array)
end
end
array1 = ["4","5","6","7"]
array2 = ["7","6","5","3"]
a1 = Array2.new(array1)
a2 = Array2.new(array2)
puts a1.to_s
puts a2.to_s
puts "The First Array Is#{a1.isreverse?(a2)?"":" Not"} The Reverse Of The Second Array"
fiddle
Here are some adjustments to your existing approach. I put in comments where I changed the original::
class Array2
def initialize (a)
#array = Array.new(a)
end
def to_array # to_s is a misnomer: it doesn't return a string
return #array
end
def isreverse (a)
#reverasea = Array.new NOTE this is not needed; the following .reverse creates a new array for you
reversea = a.to_array.reverse # get the reverse of the array represented
# NOTE = is assign, == is compare in this case
# The following compares the reversed of the array `a` with the array value of this class, #array
if (reversea == #array) then
return "The First Array Is The Reverse Of The Second Array"
else
return "The First Array Is Not The Reverse Of The Second Array"
end
end
end
array1 = ["4","5","6","7"]
array2 = ["7","6","5","3"]
a1 = Array2.new(array1)
a2 = Array2.new(array2)
puts a1.to_array # (renamed)
puts a2.to_array # (renamed)
#puts a1.isreverse(array1, array2) NOTE you don't need to pass array1 into class a1 since it is already made from array1
puts a1.isreverse(a2)
I would go for something simpler such as:
Filename: reverser.rb
class Reverser
def self.is_reverse_of(array1,array2)
array1_reversed=array1.reverse
is_or_isnt= (array1_reversed==array2)? 'Not ' : ''
return "The First Array Is #{is_or_isnt}The Reverse Of The Second Array"
end
end
puts Reverser.is_reverse_of(["4","5","6","7"], ["7","6","5","4"])
puts Reverser.is_reverse_of(["4","5","6","7"], ["7","6","5","3"])
ruby reverser.rb
The First Array Is Not The Reverse Of The Second Array
The First Array Is The Reverse Of The Second Array
The idea being to use a class level method and not instantiate as much and have less code.
I am currently learning Ruby and I'm trying to write a simple Ruby grocery_list method. Here are the instructions:
We want to write a program to help keep track of a grocery list. It takes a grocery item (like "eggs") as an argument, and returns the grocery list (that is, the item names with the quantities of each item). If you pass the same argument twice, it should increment the quantity.
def grocery_list(item)
array = []
quantity = 1
array.each {|x| quantity += x }
array << "#{quantity}" + " #{item}"
end
puts grocery_list("eggs", "eggs")
so I'm trying to figure out here how to return "2 eggs" by passing eggs twice
To help you count the different items you can use as Hash. A Hash is similar to an Array, but with Strings instead of Integers als an Index:
a = Array.new
a[0] = "this"
a[1] = "that"
h = Hash.new
h["sonja"] = "asecret"
h["brad"] = "beer"
In this example the Hash might be used for storing passwords for users. But for your
example you need a hash for counting. Calling grocery_list("eggs", "beer", "milk", "eggs")
should lead to the following commands being executed:
h = Hash.new(0) # empty hash {} created, 0 will be default value
h["eggs"] += 1 # h is now {"eggs"=>1}
h["beer"] += 1 # {"eggs"=>1, "beer"=>1}
h["milk"] += 1 # {"eggs"=>1, "beer"=>1, "milk"=>1}
h["eggs"] += 1 # {"eggs"=>2, "beer"=>1, "milk"=>1}
You can work through all the keys and values of a Hash with the each-loop:
h.each{|key, value| .... }
and build up the string we need as a result, adding
the number of items if needed, and the name of the item.
Inside the loop we always add a comma and a blank at the end.
This is not needed for the last element, so after the
loop is done we are left with
"2 eggs, beer, milk, "
To get rid of the last comma and blank we can use chop!, which "chops off"
one character at the end of a string:
output.chop!.chop!
One more thing is needed to get the complete implementation of your grocery_list:
you specified that the function should be called like so:
puts grocery_list("eggs", "beer", "milk","eggs")
So the grocery_list function does not know how many arguments it's getting. We can handle
this by specifying one argument with a star in front, then this argument will
be an array containing all the arguments:
def grocery_list(*items)
# items is an array
end
So here it is: I did your homework for you and implemented grocery_list.
I hope you actually go to the trouble of understanding the implementation,
and don't just copy-and-paste it.
def grocery_list(*items)
hash = Hash.new(0)
items.each {|x| hash[x] += 1}
output = ""
hash.each do |item,number|
if number > 1 then
output += "#{number} "
end
output += "#{item}, "
end
output.chop!.chop!
return output
end
puts grocery_list("eggs", "beer", "milk","eggs")
# output: 2 eggs, beer, milk
def grocery_list(*item)
item.group_by{|i| i}
end
p grocery_list("eggs", "eggs","meat")
#=> {"eggs"=>["eggs", "eggs"], "meat"=>["meat"]}
def grocery_list(*item)
item.group_by{|i| i}.flat_map{|k,v| [k,v.length]}
end
p grocery_list("eggs", "eggs","meat")
#=>["eggs", 2, "meat", 1]
def grocery_list(*item)
Hash[*item.group_by{|i| i}.flat_map{|k,v| [k,v.length]}]
end
grocery_list("eggs", "eggs","meat")
#=> {"eggs"=>2, "meat"=>1}
grocery_list("eggs", "eggs","meat","apple","apple","apple")
#=> {"eggs"=>2, "meat"=>1, "apple"=>3}
or as #Lee said:
def grocery_list(*item)
item.each_with_object(Hash.new(0)) {|a, h| h[a] += 1 }
end
grocery_list("eggs", "eggs","meat","apple","apple","apple")
#=> {"eggs"=>2, "meat"=>1, "apple"=>3}
Use a Hash Instead of an Array
When you want an easy want to count things, you can use a hash key to hold the name of the thing you want to count, and the value of that key is the quantity. For example:
#!/usr/bin/env ruby
class GroceryList
attr_reader :list
def initialize
# Specify hash with default quantity of zero.
#list = Hash.new(0)
end
# Increment the quantity of each item in the #list, using the name of the item
# as a hash key.
def add_to_list(*items)
items.each { |item| #list[item] += 1 }
#list
end
end
if $0 == __FILE__
groceries = GroceryList.new
groceries.add_to_list('eggs', 'eggs')
puts 'Grocery list correctly contains 2 eggs.' if groceries.list['eggs'] == 2
end
Here's a more verbose, but perhaps more readable solutions to your challenge.
def grocery_list(*items) # Notice the asterisk in front of items. It means "put all the arguments into an array called items"
my_grocery_hash = {} # Creates an empty hash
items.each do |item| # Loops over the argument array and passes each argument into the loop as item.
if my_grocery_hash[item].nil? # Returns true of the item is not a present key in the hash...
my_grocery_hash[item] = 1 # Adds the key and sets the value to 1.
else
my_grocery_hash[item] = my_grocery_hash[item] + 1 # Increments the value by one.
end
end
my_grocery_hash # Returns a hash object with the grocery name as the key and the number of occurences as the value.
end
This will create an empty hash (called dictionaries or maps in other languages) where each grocery is added as a key with the value set to one. In case the same grocery appears multiple times as a parameter to your method, the value is incremented.
If you want to create a text string and return that instead of the hash object and you can do like this after the iteration:
grocery_list_string = "" # Creates an empty string
my_grocery_hash.each do |key, value| # Loops over the hash object and passes two local variables into the loop with the current entry. Key being the name of the grocery and value being the amount.
grocery_list_string << "#{value} units of #{key}\n" # Appends the grocery_list_string. Uses string interpolation, so #{value} becomes 3 and #{key} becomes eggs. The remaining \n is a newline character.
end
return grocery_list_string # Explicitly declares the return value. You can ommit return.
Updated answer to comment:
If you use the first method without adding the hash iteration you will get a hash object back which can be used to look up the amount like this.
my_hash_with_grocery_count = grocery_list("Lemonade", "Milk", "Eggs", "Lemonade", "Lemonade")
my_hash_with_grocery_count["Milk"]
--> 1
my_hash_with_grocery_count["Lemonade"]
--> 3
Enumerable#each_with_object can be useful for things like this:
def list_to_hash(*items)
items.each_with_object(Hash.new(0)) { |item, list| list[item] += 1 }
end
def hash_to_grocery_list_string(hash)
hash.each_with_object([]) do |(item, number), result|
result << (number > 1 ? "#{number} #{item}" : item)
end.join(', ')
end
def grocery_list(*items)
hash_to_grocery_list_string(list_to_hash(*items))
end
p grocery_list('eggs', 'eggs', 'bread', 'milk', 'eggs')
# => "3 eggs, bread, milk"
It iterates an array or hash to enable building another object in a convenient way. The list_to_hash method uses it to build a hash from the items array (the splat operator converts the method arguments to an array); the hash is created so that each value is initialized to 0. The hash_to_grocery_list_string method uses it to build an array of strings that is joined to a comma-separated string.
I am wondering how one would search through an array of hashes and return a value based on a search string. For example, #contacts contains the hash elements: :full_name, :city, and :email. The variable #contacts (I guess it would be an array) contains three entries (perhaps rows). Below is the code I have so far to conduct a search based on :city value. However it's not working. Can anyone give me an idea what's going on?
def search string
#contacts.map {|hash| hash[:city] == string}
end
You should use select instead of map:
def search string
#contacts.select { |hash| hash[:city] == string }
end
In your code you tried to map (or transform) your array using a block, which yields boolean values. map takes a block and invokes the block for each element of self, constructing a new array containing elements returned by the block. As the result, you got an array of booleans.
select works similar. It takes a block and iterates over the array as well, but instead of transforming the source array it returns an array containing elements for which the block returns true. So it's a selection (or filtering) method.
In order to understand the difference between these two methods it's useful to see their example definitions:
class Array
def my_map
[].tap do |result|
self.each do |item|
result << (yield item)
end
end
end
def my_select
[].tap do |result|
self.each do |item|
result << item if yield item
end
end
end
end
Example usage:
irb(main):007:0> [1,2,3].my_map { |x| x + 1 }
[2, 3, 4]
irb(main):008:0> [1,2,3].my_select { |x| x % 2 == 1 }
[1, 3]
irb(main):009:0>
You can try this:
def search string
#contacts.select{|hash| h[:city].eql?(string) }
end
This will return an array of hashes which matches string.
There is an array with 2 elements
test = ["i am a boy", "i am a girl"]
I want to test if a string is found inside the array elements, say:
test.include("boy") ==> true
test.include("frog") ==> false
Can i do it like that?
Using Regex.
test = ["i am a boy" , "i am a girl"]
test.find { |e| /boy/ =~ e } #=> "i am a boy"
test.find { |e| /frog/ =~ e } #=> nil
Well you can grep (regex) like this:
test.grep /boy/
or even better
test.grep(/boy/).any?
Also you can do
test = ["i am a boy" , "i am a girl"]
msg = 'boy'
test.select{|x| x.match(msg) }.length > 0
=> true
msg = 'frog'
test.select{|x| x.match(msg) }.length > 0
=> false
I took Peters snippet and modified it a bit to match on the string instead of the array value
ary = ["Home:Products:Glass", "Home:Products:Crystal"]
string = "Home:Products:Glass:Glasswear:Drinking Glasses"
USE:
ary.partial_include? string
The first item in the array will return true, it does not need to match the entire string.
class Array
def partial_include? search
self.each do |e|
return true if search.include?(e.to_s)
end
return false
end
end
If you don't mind to monkeypatch the the Array class you could do it like this
test = ["i am a boy" , "i am a girl"]
class Array
def partial_include? search
self.each do |e|
return true if e[search]
end
return false
end
end
p test.include?("boy") #==>false
p test.include?("frog") #==>false
p test.partial_include?("boy") #==>true
p test.partial_include?("frog") #==>false
If you want to test if a word included into the array elements, you can use method like this:
def included? array, word
array.inject([]) { |sum, e| sum + e.split }.include? word
end
Excuse the newbie question. I'm trying to create a two dimensional array in ruby, and initialise all its values to 1. My code is creating the two dimensional array just fine, but fails to modify any of its values.
Can anyone explain what I'm doing wrong?
def mda(width,height)
#make a two dimensional array
a = Array.new(width)
a.map! { Array.new(height) }
#init all its values to 1
a.each do |row|
row.each do |column|
column = 1
end
end
return a
end
It the line row.each do |column| the variable column is the copy of the value in row. You can't edit its value in such way. You must do:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.each do |row|
row.map!{1}
end
return a
end
Or better:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
a = Array.new(width){ Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
Array.new(width) { Array.new(height){1} }
end
each passes into the block parameter the value of each element, not the element itself, so column = 1 doesn't actually modify the array.
You can do this in one step, though - see the API docs for details on the various forms of Array#new. Try a = Array.new(width) {|i| Array.new(height) {|j| 1 } }
you can create it like this?
a=Array.new(width) { Array.new(height,1) }
column in your nested each loop is a copy of the value at that place in the array, not a pointer/reference to it, so when you change its value you're only changing the value of the copy (which ceases to exist outside the block).
If you just want a two-dimensional array populated with 1s something as simple as this will work:
def mda(width,height)
[ [1] * width ] * height
end
Pretty simple.
By the way, if you want to know how to modify the elements of a two-dimensional array as you're iterating over it, here's one way (starting from line 6 in your code):
#init all its values to 1
a.length.times do |i|
a[i].length.times do |j|
a[i][j] = 1
end
end