can you define a block inline with ruby? - ruby

Is it possible to define a block in an inline statement with ruby? Something like this:
tasks.collect(&:title).to_block{|arr| "#{arr.slice(0, arr.length - 1).join(", ")} and #{arr.last}" }
Instead of this:
titles = tasks.collect(&:title)
"#{titles.slice(0, titles.length - 1).join(", ")} and #{titles.last}"
If you said tasks.collect(&:title).slice(0, this.length-1) how can you make 'this' refer to the full array that was passed to slice()?
Basically I'm just looking for a way to pass the object returned from one statement into another one, not necessarily iterating over it.

You're kind of confusing passing a return value to a method/function and calling a method on the returned value. The way to do what you described is this:
lambda {|arr| "#{arr.slice(0, arr.length - 1).join(", ")} and #{arr.last}"}.call(tasks.collect(&:title))
If you want to do it the way you were attempting, the closest match is instance_eval, which lets you run a block within the context of an object. So that would be:
tasks.collect(&:title).instance_eval {"#{slice(0, length - 1).join(", ")} and #{last}"}
However, I would not do either of those, as it's longer and less readable than the alternative.

I'm not sure exactly what you're trying to do, but:
If you said tasks.collect(&:title).slice(0, this.length-1) how can you make 'this' refer to the full array that was passed to slice()?
Use a negative number:
tasks.collect(&:title)[0..-2]
Also, in:
"#{titles.slice(0, titles.length - 1).join(", ")} and #{titles.last}"
you've got something weird going on with your quotes, I think.

I don't really understand why you would want to, but you could add a function to the ruby classes that takes a block, and passes itself as a parameter...
class Object
def to_block
yield self
end
end
At this point you would be able to call:
tasks.collect(&:title).to_block{|it| it.slice(0, it.length-1)}
Of course, modifying the Object class should not be taken lightly as there can be serious consequences when combining with other libraries.

Although there are many good answers here, perhaps you're looking for something more like this in terms of an objective:
class Array
def andjoin(separator = ', ', word = ' and ')
case (length)
when 0
''
when 1
last.to_s
when 2
join(word)
else
slice(0, length - 1).join(separator) + word + last.to_s
end
end
end
puts %w[ think feel enjoy ].andjoin # => "think, feel and enjoy"
puts %w[ mitchell webb ].andjoin # => "mitchell and webb"
puts %w[ yes ].andjoin # => "yes"
puts %w[ happy fun monkeypatch ].andjoin(', ', ', and ') # => "happy, fun, and monkeypatch"

Related

Convert a letter to its corresponding control code

Given a single letter (string), say "a", I want to convert this into its corresponding control code, i.e. "\ca" - or equivalently (in alternate syntax) - "\C-a", ?\ca, "\x01", "\u0001"
I was hoping there'd be some "nice", clean way of doing this conversion, but I can't figure it out.
An obvious first attempt might be to try something like:
def convert_to_control_code(letter)
"\c#{letter}"
end
...But this does not work, since this will always return "\u0003{letter}" (where "\u0003" is the control code "\c#"
My current solution is simply to "brute force" it by doing the following:
def convert_to_control_code(letter)
(0..255).detect { |x| x.chr =~ Regexp.new("\\c#{char}") }.chr
end
However, I can't help but feel there's a "right" way of doing this!
Edit:
Here's another, non brute-force solution I've come up with, that seems to work:
def convert_to_control_code(letter)
(letter.ord % 32).chr
end
This looks much nicer, but also very hacky!
You can write it as :
def convert_to_control_code(letter)
eval "?\\C-#{letter.chr}"
end
convert_to_control_code(97) # => "\u0001"
convert_to_control_code(98) # => "\u0002"
One possibility is to do the same as Ruby itself does. It might look something like this:
def convert_to_control(letter)
letter = letter.chr # ensure we are only dealing with a single char
return 0177.chr if letter == '?'
raise 'an error' unless letter.ascii_only? # or do something else
(letter.ord & 0x9f).chr
end
You might want to change the encoding of the result depending on what you are doing.

Push an array into another array with Ruby, and return square brackets

I've spent a few hours searching for a way to push an array into another array or into a hash. Apologies in advance if the formatting of this question is bit messy. This is the first time I've asked a question on StackOverflow so I'm trying to get the hang of styling my questions properly.
I have to write some code to make the following test unit past:
class TestNAME < Test::Unit::TestCase
def test_directions()
assert_equal(Lexicon.scan("north"), [['direction', 'north']])
result = Lexicon.scan("north south east")
assert_equal(result, [['direction', 'north'],
['direction', 'south'],
['direction', 'east']])
end
end
The most simple thing I've come up with is below. The first part passes, but then the second part is not returning the expected result when I run rake test.
Instead or returning:
[["direction", "north"], ["direction", "south"], ["direction",
"east"]]
it's returning:
["north", "south", "east"]
Although, if I print the result of y as a string to the console, I get 3 separate arrays that are not contained within another array (as below). Why hasn't it printed the outermost square brackets of the array, y?
["direction", "north"]
["direction", "south"]
["direction", "east"]
Below is the code I've written in an attempt to pass the test unit above:
class Lexicon
def initialize(stuff)
#words = stuff.split
end
def self.scan(word)
if word.include?(' ')
broken_words = word.split
broken_words.each do |word|
x = ['direction']
x.push(word)
y = []
y.push(x)
end
else
return [['direction', word]]
end
end
end
Any feedback about this will be much appreciated. Thank you all so much in advance.
What you're seeing is the result of each, which returns the thing being iterated over, or in this case, broken_words. What you want is collect which returns the transformed values. Notice in your original, y is never used, it's just thrown out after being composed.
Here's a fixed up version:
class Lexicon
def initialize(stuff)
#words = stuff.split
end
def self.scan(word)
broken_words = word.split(/\s+/)
broken_words.collect do |word|
[ 'direction', word ]
end
end
end
It's worth noting a few things were changed here:
Splitting on an arbitrary number of spaces rather than one.
Simplifying to a single case instead of two.
Eliminating the redundant return statement.
One thing you might consider is using a data structure like { direction: word } instead. That makes referencing values a lot easier since you'd do entry[:direction] avoiding the ambiguous entry[1].
If you're not instantiating Lexicon objects, you can use a Module which may make it more clear that you're not instantiating objects.
Also, there is no need to use an extra variable (i.e. broken_words), and I prefer the { } block syntax over the do..end syntax for functional blocks vs. iterative blocks.
module Lexicon
def self.scan str
str.split.map {|word| [ 'direction', word ] }
end
end
UPDATE: based on Cary's comment (I assume he meant split when he said scan), I've removed the superfluous argument to split.

What does |lang| part mean in this each method? Where does this come from?

I am reading through Chris Pine's Learn To Program chapter 7 Arrays and Iterators.
He introduces the each method with the following example:
languages = ['English', 'German', 'Ruby']
languages.each do |lang|
puts 'I love ' + lang + '!'
puts 'Don\'t you?'
end
puts 'And let\'s hear it for C++!'
puts '...'
It's not hard to understand how it works overall, but I can't figure out where the |lang| part is coming from so out of blue. Shouldn't it be assigned/named or something before it can be used like this? So the computer can know what the "lang" refers to? Does || do something wrapping around lang? Or does ruby just know what lang means?
I am afraid the question is too basic, but I am hoping someone might help me just a bit...
lang is a variable used to hold an element from the languages array. Any variable inside || will be used to grab single element from array. So, every time the loops executes, an element from the array is popped out and held in an variable named lang and data held by lang is displayed using puts method.
The each method yields every element one by one and it gets assigned to the variable lang.
Internally, the each method is implemented something like this:
def each
index = 0
while index < array.length
yield array[index]
index += 1
end
end
|lang| is a block variable. If you strip down your code, you can see that the .each method is iterating over the languages array and assigning array elements to the block variable:
languages = ['English', 'German', 'Ruby']
languages.each do |lang|
puts lang
end
#=> English
#=> German
#=> Ruby
Multi-line blocks use a do/end syntax (as in your example), and single-line blocks use a braces syntax. For example:
languages = ['English', 'German', 'Ruby']
languages.each { |lang| puts lang}
It sounds like, in the above example, you created an array storing multiple language variables.
You then iterated over all three elements in the array and represented each one with a variable called lang.
lang, which is inside the brackets is simply a variable.
Hope this helped you

Ruby - Populate and Array with returned method values

So, pretend we have the following three methods that check a grid to determine if there is a winner, and will return true if there is.
def win_diagonal?
# Code here to check for diagonal win.
end
def win_horizontal?
# Code here to check for horizontal win.
end
def win_vertical?
# Code here to check for vertical win.
end
I would like to push the returned values of each method into an Array instead of literally using the method names. Is this possible?
def game_status
check_wins = [win_vertical?, win_diagonal?, win_horizontal?]
if check_wins.uniq.length != 1 # When we don't have only false returns from methods
return :game_over
end
end
What you are looking for will indeed work in ruby.
def hello_world?
"hello world!"
end
a = [hello_world?]
Prints out
=> ["hello world!"]
Hope that helps. IRB is your friend when you wonder if something is possible in Ruby :-)
Simpler way (and very readable) yet:
def game_status
win_vertical? || win_diagonal? || win_horizontal?
end
If, for example, win_vertical? returns true, the other algorithms won't even need to run. You return immediately.
Or, if you need to know in which way the user won, I mean, if you need to preserve the results of all methods after they ran, you can use a hash, like:
{:vertical => win_vertical?, :diagonal => win_diagonal?, :horizontal => win_horizontal?}
This solution, like the array one, is worse than the first one above for it runs all algorithms all the time. If they are complex, you may have a problem. =)
You can do something like this when you really want to store all return values in an array:
def game_status
check_wins = [win_vertical?, win_diagonal?, win_horizontal?]
return :game_over if check_wins.any?
end
For readability I would prefer:
def game_status
return :game_over if win_vertical? || win_diagonal? || win_horizontal?
end

Ruby refactoring sentence manipulation

I have a bit of a brain buster i am trying to refactor this method. there are several goals behind doing this, first is so that if anyone reads this code once they can move on because they wouldn't have questions about it.
Second i am hoping there is a faster way of doing this. i mean in my mind its almost like a p vs np thing, but i am sure there are neater ways of accomplishing it. Applying the Solid principles is the goal.
This method receives a string and break it down into individual words. Then it inspects each of the words to find the following suffixes -er, -ers, -ed, -ant, -and and -anned. with each of these suffixes there is a new replacement -er becomes -xor, -ers becomes -xors, -ed becomes -d, -ant, -and and -anned becomes -&(eg. banned becomes b&)
This is what i have so far for the -er and -ers suffixes. its ugly and i bet really slow.
def reconstruct_sentence
s = #sentence.split(/\W+/)
s.each_with_index do |word, i|
if word.end_with?("er")
s[i] = word.chomp("er") + ("xor")
elsif word.end_with?("ers")
s[i] = word.chomp("ers") + ("xors")
else
return
end
s.join(" ")
end
end
I think what i am asking for is far out there and refactoring takes i think a few years of code experience to get used to. but as i go along i can see that this method has more then one purpose breaking solid. so i broke it like this.
def edit_sentence
split_sentence(#sentence) #this would be the sentence that is initialized
sentence.each_with_index do |word, i|
if word.end_with?("er")
sentence[i] = word.chomp("er") + ("xor")
elsif word.end_with?("ers")
sentence[i] = word.chomp("ers") + ("xors")
else
return
end
reconstruct_sentence
end
end
def split_sentence(sentence)
sentence.split(/\W+/)
end
def reconstruct_sentence
sentence.join(" ")
end
the part that i am struggling to re-factor is getting all the suffixes in one method call. i first thought with all the repetition i should use a hash to save all the old suffixes as a key and the new ones as a value but i reckon its going to get complicated like meta programming to the max. any advice? and does anyone know a good book on refactoring patterns?
thanks in advance.
First, your code doesn't work properly, since the return keyword returns from the whole method, instead of continuing.
For a short version of what you try to do, you can use gsub with replacements:
def reconstruct_sentence
#sentence.gsub(/ers?\b/, 'er' => 'xor', 'ers' => 'xors')
end
#sentence = 'this is a tester without any eaters'
reconstruct_sentence
# => "this is a testxor without any eatxors"
More generically you can do:
def reconstruct_sentence
replacements = {'er' => 'xor', 'ers' => 'xors', 'ed' => 'd',
'ant' => '&', 'and' => '&', 'anned' => '&'}
#sentence.gsub(Regexp.union(replacements.keys), replacements)
end
#sentence = 'this is a tester without any planned fighters'
reconstruct_sentence
# => "this is a testxor without any pl& fightxors"

Resources