I thought that doing puts #{a} would result in the same output as puts a, but found this not to be the case. Consider:
irb(main):001:0> a = [1,2]
=> [1, 2]
irb(main):002:0> puts a
1
2
=> nil
irb(main):003:0> puts "#{a}"
12
=> nil
irb(main):004:0>
In the above example it doesn't matter much, but it may matter when I want to print multiple variables on one line, such as (psudocode):
puts "There are #{a.size} items in the whitelist: #{a}"
Why is the output different here? Do they actually do different things, or have different semantics?
That's because "#{a}" calls the #to_s method on the expression.
So:
puts a # equivalent to, well, puts a
puts "#{a}" # equivalent to the next line
puts a.to_s
Update:
To elaborate, puts eventually calls #to_s but it adds logic in front of the actual output, including special handling for arrays. It just happens that Array#to_s doesn't use the same algorithm. (See puts docs here.) Here is exactly what it does...
rb_io_puts(int argc, VALUE *argv, VALUE out)
{
int i;
VALUE line;
/* if no argument given, print newline. */
if (argc == 0) {
rb_io_write(out, rb_default_rs);
return Qnil;
}
for (i=0; i<argc; i++) {
if (TYPE(argv[i]) == T_STRING) {
line = argv[i];
goto string;
}
line = rb_check_array_type(argv[i]);
if (!NIL_P(line)) {
rb_exec_recursive(io_puts_ary, line, out);
continue;
}
line = rb_obj_as_string(argv[i]);
string:
rb_io_write(out, line);
if (RSTRING_LEN(line) == 0 ||
!str_end_with_asciichar(line, '\n')) {
rb_io_write(out, rb_default_rs);
}
}
return Qnil;
}
Related
I've been practicing some algorithms with ruby for a while, and I'm wondering if it is possible to catch the returned value from within the method.
the code below is to reverse a string without any kind of reverse method and with few local variables...
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
end
Note that the result of the 'each' method is not being assigned to any variable. So, 'each' evaluates to an array with a reversed sequence of characters. At the 'end' (literally) I've just 'called' the method 'join' to glue everything together. The idea is to 'catch' the returned value from all this process and check if is true or false that the reversed string is a palindrome.
If the reversed string is equal to the original one then the word is a palindrome. Ex. "abba", "sexes", "radar"...
for example:
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
# catch here the returned value from the code above
# and check if its a palindrome or not. (true or false)
end
Thank you guys! I will be very grateful if anyone could help me figure out this!
Just add == a to see if your reversal matches the original string:
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join == a
end
puts rev("racecar") # => true
puts rev("racecars") # => false
An easier way to check palindromes (rev could be better named palindrome?) is a == a.reverse since .reverse is essentially what your split/each/join does.
If you want back all the information, you can return an array with both the values:
def rev(a)
i = -1
rev = a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
[rev, rev == a] # or
# return rev, rev == a
end
p rev("abra") #=> ["arba", false]
p rev("abba") #=> ["abba", true]
You can also return a hash:
{ reverse: rev, palindrome: rev == a}
to get
#=> {:reverse=>"arba", :palindrome=>false}
#=> {:reverse=>"abba", :palindrome=>true}
Here are a couple of other ways you could reverse a string.
#1
def esrever(str)
s = str.dup
(str.size/2).times { |i| s[i], s[-1-i] = s[-1-i], s[i] }
s
end
esrever("abcdefg")
#=> "gfedcba"
esrever("racecar")
#=> "racecar"
This uses parallel assignment (sometimes called multiple assignment).
#2
def esrever(str)
a = str.chars
''.tap { |s| str.size.times { s << a.pop } }
end
esrever("abcdefg")
#=> "gfedcba"
esrever("racecar")
#=> "racecar"
I've used Object#tap merely to avoid creating a local variable initialized to an empty string and then having to make that variable the last line of the method.
With both methods a string str is a palindrome if and only if str == esrever(str).
This function should take in two strings "daBcD" and "ABC". It is trying to create the string "b" from the letters in "a". You can only delete or capitalize letters, you cant change them. b will always contain all uppercase letters.
def abbreviation(a, b)
aArray = a.split('')
idx = 0
aArray.each do |char|
#print "char: #{char}\n"
#print "Before loops: #{aArray}\n"
if char.casecmp(b[idx]) == 0
char.upcase!
idx += 1
#print "char: #{char}\nArry: #{aArray}\n"
#print "idx: #{idx}\n siz: #{b.size}\n"
if idx == b.size
aArray.reject! {|i| i == 'delete'}
aArray.slice!(b.size)
break
end
else
aArray[aArray.index(char)] = 'delete'
#print "deleted, now is: #{aArray}\n"
end
end
res = aArray.join('')
if res == b
return 'YES'
else
return 'NO'
end
end
This works for a couple test cases, but fails most of them. Can someone describe a better approach?
I have assumed the problem is to determine whether the characters in b appear in a (case indifferent), in the same order as in b, but not necessarily contiguous in a (see the second example below). If they do I return an array of the indices at which they appear in a. If there is no match, nil is returned.
def doit(a, b)
m = a.match(Regexp.new(b.each_char.map { |c| "(#{c})" }.join('.*'),
Regexp::IGNORECASE))
return nil if m.nil?
(1..b.size).map { |i| m.begin(i) }
end
doit "daBcD", "ABC"
#=> [1, 2, 3]
doit "daXBDecf", "ABC"
#=> [1, 3, 6]
doit "dacBD", "ABC"
#=> nil
For the first example the regular expression is as follows.
Regexp.new("ABC".each_char.map { |c| "(#{c})" }.join('.*'), Regexp::IGNORECASE)
#=> /(A).*(B).*(C)/i
The absolutely easiest way is via regular expression:
def abbreviation(a, b)
re = Regexp.new(b.each_char.map(&Regexp.method(:quote)).join('.*'), Regexp::IGNORECASE)
!!re.match(a)
end
abbreviation("daBcD", "ABC")
# => true
abbreviation("daCbD", "ABC")
# => false
For the input ABC, we'll construct a regular expression /A.*B.*C/i, then test the other string against it. The .* construct will account for "deletion"; the IGNORECASE option for "capitalisation".
EDIT: If the problem is further constrained that only lowercase letters can be deleted, as suggested by the comments,
def abbreviation(a, b)
# (b is uppercase only)
re_pat = b.each_char.map { |c| "[#{c}#{c.downcase}]"}.join('[[:lower:]]*')
re = Regexp.new(re_pat)
!!re.match(a)
end
p abbreviation("daBcD", "ABC") # => true
p abbreviation("daBdcD", "ABC") # => true
p abbreviation("daBDcD", "ABC") # => false
If I have a string like this
str =<<END
7312357006,1.121
3214058234,3456
7312357006,1234
1324958723,232.1
3214058234,43.2
3214173443,234.1
6134513494,23.2
7312357006,11.1
END
If a number in the first value shows up again, I want to add their second values together. So the final string would look like this
7312357006,1246.221
3214058234,3499.2
1324958723,232.1
3214173443,234.1
6134513494,23.2
If the final output is an array that's fine too.
There are lots of ways to do this in Ruby. One particularly terse way is to use String#scan:
str = <<END
7312357006,1.121
3214058234,3456
7312357006,1234
1324958723,232.1
3214058234,43.2
3214173443,234.1
6134513494,23.2
7312357006,11.1
END
data = Hash.new(0)
str.scan(/(\d+),([\d.]+)/) {|k,v| data[k] += v.to_f }
p data
# => { "7312357006" => 1246.221,
# "3214058234" => 3499.2,
# "1324958723" => 232.1,
# "3214173443" => 234.1,
# "6134513494" => 23.2 }
This uses the regular expression /(\d+),([\d.]+)/ to extract the two values from each line. The block is called with each pair as arguments, which are then merged into the hash.
This could also be written as a single expression using each_with_object:
data = str.scan(/(\d+),([\d.]+)/)
.each_with_object(Hash.new(0)) {|(k,v), hsh| hsh[k] += v.to_f }
# => (same as above)
There are likewise many ways to print the result, but here are a couple I like:
puts data.map {|kv| kv.join(",") }.join("\n")
# => 7312357006,1246.221
# 3214058234,3499.2
# 1324958723,232.1
# 3214173443,234.1
# 6134513494,23.2
# or:
puts data.map {|k,v| "#{k},#{v}\n" }.join
# => (same as above)
You can see all of these in action on repl.it.
Edit: Although I don't recommend either of these for the sake of readability, here's more just for kicks (requires Ruby 2.4+):
data = str.lines.group_by {|s| s.slice!(/(\d+),/); $1 }
.transform_values {|a| a.sum(&:to_f) }
...or, to going straight to a string:
puts str.lines.group_by {|s| s.slice!(/(\d+),/); $1 }
.map {|k,vs| "#{k},#{vs.sum(&:to_f)}\n" }.join
Since repl.it is stuck on Ruby 2.3: Try it online!
You could achieve this using each_with_object, as below:
str = "7312357006,1.121
3214058234,3456
7312357006,1234
1324958723,232.1
3214058234,43.2
3214173443,234.1
6134513494,23.2
7312357006,11.1"
# convert the string into nested pairs of floats
# to briefly summarise the steps: split entries by newline, strip whitespace, split by comma, convert to floats
arr = str.split("\n").map(&:strip).map { |el| el.split(",").map(&:to_f) }
result = arr.each_with_object(Hash.new(0)) do |el, hash|
hash[el.first] += el.last
end
# => {7312357006.0=>1246.221, 3214058234.0=>3499.2, 1324958723.0=>232.1, 3214173443.0=>234.1, 6134513494.0=>23.2}
# You can then call `to_a` on result if you want:
result.to_a
# => [[7312357006.0, 1246.221], [3214058234.0, 3499.2], [1324958723.0, 232.1], [3214173443.0, 234.1], [6134513494.0, 23.2]]
each_with_object iterates through each pair of data, providing them with access to an accumulator (in this the hash). By following this approach, we can add each entry to the hash, and add together the totals if they appear more than once.
Hope that helps - let me know if you've any questions.
def combine(str)
str.each_line.with_object(Hash.new(0)) do |s,h|
k,v = s.split(',')
h.update(k=>v.to_f) { |k,o,n| o+n }
end.reduce('') { |s,kv_pair| s << "%s,%g\n" % kv_pair }
end
puts combine str
7312357006,1246.22
3214058234,3499.2
1324958723,232.1
3214173443,234.1
6134513494,23.2
Notes:
using String#each_line is preferable to str.split("\n") as the former returns an enumerator whereas the latter returns a temporary array. Each element generated by the enumerator is line of str that (unlike the elements of str.split("\n")) ends with a newline character, but that is of no concern.
see Hash::new, specifically when a default value (here 0) is used. If a hash has been defined h = Hash.new(0) and h does not have a key k, h[k] returns the default value, zero (h is not changed). When Ruby encounters the expression h[k] += 1, the first thing she does is expand it to h[k] = h[k] + 1. If h has been defined with a default value of zero, and h does not have a key k, h[k] on the right of the equality (syntactic sugar1 for h.[](k)) returns zero.
see Hash#update (aka merge!). h.update(k=>v.to_f) is syntactic sugar for h.update({ k=>v.to_f })
see Kernel#sprint for explanations of the formatting directives %s and %g.
the receiver for the expression reduce('') { |s,kv_pair| s << "%s,%g\n" % kv_pair } (in the penultimate line), is the following hash.
{"7312357006"=>1246.221, "3214058234"=>3499.2, "1324958723"=>232.1,
"3214173443"=>234.1, "6134513494"=>23.2}
1 Syntactic sugar is a shortcut allowed by Ruby.
Implemented this solution as hash was giving me issues:
d = []
s.split("\n").each do |line|
x = 0
q = 0
dup = false
line.split(",").each do |data|
if x == 0 and d.include? data then dup = true ; q = d.index(data) elsif x == 0 then d << data end
if x == 1 and dup == false then d << data end
if x == 1 and dup == true then d[q+1] = "#{'%.2f' % (d[q+1].to_f + data.to_f).to_s}" end
if x == 2 and dup == false then d << data end
x += 1
end
end
x = 0
s = ""
d.each do |val|
if x == 0 then s << "#{val}," end
if x == 1 then s << "#{val}\n ; x = 0" end
x += 1
end
puts(s)
Take a good look at the snippet below:
a = [1,2,3]
n = 2
puts a.find { |i| i == n }
=> 2
a = [1,2,3]
n = [2]
puts a.find { |i| i == n.shift }
=> nil
Tip: you can see a running version here http://repl.it/OL3
Now explain it. Why the second #find returns nil ?
Because Array#shift removes the element from the array.
So, the first time the block executes, it is comparing e['name'] == "pets" but on the next iteration, it is comparing e['name'] == nil. Unless e['name'] is "pets" on the first iteration, the .find will return nil.
#Charles Caldwell explained correctly, why you did get that result. Now just use Array#first method as below which is safe :
a.find { |i| i == n.first }
I am trying to make this code return when called without a block. The uncommented lines at the bottom is what I'm trying to get to return. The first uncommented line should return in tut, second line converted to english and the last should be in english. And why is the line " puts eng " returning up and down and not in sentence form? Thanks for any and all help.
Here's my code:
class Tut
##consonants = ["b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"]
def is_tut? string
if string =~ /^(([b-df-hj-np-z]ut)|([aeiou\s])|[[:punct:]])+$/i
yield
else
false
end
end
def self.to_tut string
string.each_char do |c|
c += "ut" if ##consonants.find { |i| i == c.downcase }
yield c if block_given?
end
end
def self.to_english string
array = string.split //
array.each do |c|
if ##consonants.find { |i| i == c.downcase }
array.shift
array.shift
end
yield c if block_given?
end
end
end
#Tut.to_tut( "Wow! Look at this get converted to Tut!" ) { |c| print c }
# should output : Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!
#puts
#puts
tut = Tut.to_tut( "Wow! Look at this get converted to Tut!" )
puts "from return: #{tut}"
puts
#Tut.to_tut( "Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!" ) { |c| print c }
#should outout : Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!
#puts
#puts
tut = Tut.to_tut( "Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!" )
puts "from return: #{tut}"
#puts
#tut_string = ""
#Tut.to_tut( "I'm in tut but I want to be in english." ) { |c| tut_string += c }
#puts tut_string
# should output : I'mut inut tututut bututut I wutanuttut tuto bute inut enutgutlutisuthut.
puts
#Tut.to_english( tut_string ){ |c| print c }
# should output : I'm in tut but I want to be in english.
lan = Tut.to_english( tut )
puts lan
(Opening note: You normally don't want to modify an Enumerable object while iterating over it, since that makes it much harder to read the code and debug it.)
Your to_tut doesn't retain your modifications because the "c" block variable is a copy of the string slice, instead of a reference to part of the string (if it were a ref, you'd be able to use << to append; "+=" still wouldn't work because it reassigns rather than changing the ref). That's just how each_char works, since a String doesn't contain references.
If you wanted to modify the string in place, you'd probably have to count backwards and then insert the 'ut' by index via string#[]= . But that's way complicated so I'll present a couple alternates.
Working to_tut #1:
def self.to_tut string
string.chars.map do |c|
yield c if block_given?
# this must be the last expression the block
if ##consonants.find { |i| i == c.downcase }
c + 'ut'
else
c
end
end.join
end
Working to_tut #2 - this is probably the most ruby-ish way to do it:
def self.to_tut string
string.gsub(/[#{##consonants.join}]/i) {|match|
yield match if block_given?
# this must be the last expression in the block
match + 'ut'
}
end
Your to_english doesn't work because array.shift always removes the first element of the array. Instead, you want to track the current index, and remove 2 chars starting from index+1.
Working to_english:
def self.to_english2 string
array = string.split //
array.each_with_index do |c, idx|
if ##consonants.find { |i| i == c.downcase }
array.slice!(idx+1, 2)
end
yield c if block_given?
end
array.join
end
Regarding why your "puts lan" returns one char per line - it's because your to_english returns an array. You'll want to call join to convert it.
The methods to_tut and to_english are giving you wrong answers when used without a block. This happens because ruby always returns the last value evaluated in your method. In your code that will be the result of the string.each_char for to_tut or array.each for to_english. In both cases, the result contains the original input, which is consequently returned and printed.
As to the puts eng, it prints the array returned by array.each of to_english.