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 }
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).
I'm trying to dynamically generate a case statement based on an array of values. For example let's say I have an array of ranges
[1..3,4..6,7..20,21..38]
and I want to write a dynamic case statement that returns the first number of whatever range
case n
ranges.each do |r|
when r
r.first
end
end
Is this possible, or will I have to find another way to do it (my actual code is more complex)?
If i get your question right, then you can forget case statement and do it using detect:
ary = [1..3, 4..6, 7..20, 21..38]
num = 15 # say
ary.detect { |sub_ary| sub_ary.include?(num) }
=> 7..20
ary.detect { |sub_ary| sub_ary.include?(num) }.first # call `first` on result of above, which is a range, to get the first element.
=> 7
Just out of curiosity:
number = 5
instance_eval [
"case number",
*ranges.map { |r| "when #{r} then (#{r}).first" },
"end"
].join($/)
#⇒ 4
In addition to #detect (or #find) with #include? from Jagdeep Singhs answer you can also use the case equality operator (Range#===). This operator is used by the case statement to compare the input value with the scenario's you're providing.
ranges.find { |range| range === n }.first
Keep in mind both #detect and #find return nil if no value can be found. This means you might want to use the safe navigation operator (}&.first) to prevent a no method exception of #first on nil if the value can't be found.
Well, this works, but is kind of pointless and thread unsafe:
def get_range(n)
ranges = [1..3,4..6,7..20,21..38]
case n
when 3
# special case
199
when ->(x) { #_get_range = ranges.find { |r| r.cover?(x) } }
#_get_range.first
else
0
end
ensure
remove_instance_variable(:#_get_range) if instance_variable_defined?(:#_get_range)
end
get_range(3) # => 199
get_range(5) # => 4
get_range(50) # => 0
You could just do:
ranges.find { |r| r.cover?(n) }&.first || 0
My two cents..
ranges = [1..3,4..6,7..20,21..38]
num = 15
ranges.bsearch { |range| range.member? num }.begin
I have this array of hashes:
results = [
{"day"=>"2012-08-15", "name"=>"John", "calls"=>"5"},
{"day"=>"2012-08-15", "name"=>"Bill", "calls"=>"8"},
{"day"=>"2012-08-16", "name"=>"Bill", "calls"=>"11"},
]
How can I search the results to find how many calls Bill made on the 15th?
After reading the answers to "Ruby easy search for key-value pair in an array of hashes", I think it might involve expanding upon the following find statement:
results.find { |h| h['day'] == '2012-08-15' }['calls']
You're on the right track!
results.find {|i| i["day"] == "2012-08-15" and i["name"] == "Bill"}["calls"]
# => "8"
results.select { |h| h['day'] == '2012-08-15' && h['name'] == 'Bill' }
.reduce(0) { |res,h| res += h['calls'].to_i } #=> 8
A Really clumsy implementation ;)
def get_calls(hash,name,date)
hash.map{|result| result['calls'].to_i if result['day'] == date && result["name"] == name}.compact.reduce(:+)
end
date = "2012-08-15"
name = "Bill"
puts get_calls(results, name, date)
=> 8
Or another possible way, but a little worse, using inject:
results.inject(0) { |number_of_calls, arr_element| arr_element['day'] == '2012-08-15' ? number_of_calls += 1 : number_of_calls += 0 }
Note that you have to set number_of_calls in each iteration, otherwise it will not work, for example this does NOT work:
p results.inject(0) { |number_of_calls, arr_element| number_of_calls += 1 if arr_element['day'] == '2012-08-15'}
Actually, "reduce" or "inject" is specifically for this exact operation (To reduce the contents of an enumerable down into a single value:
results.reduce(0) do |count, value|
count + ( value["name"]=="Bill" && value["day"] == "2012-08-15" ? value["calls"].to_i : 0)
end
Nice writeup here:
"Understanding map and reduce"
I want to map elements of an array such that all elements
of the array are floats, except the first element which
is a string.
Anyone know how I can do this?
Tried this but doesn't work:
arr = arr.map { |e| e.to_i if e != arr.first }
Another solution is
[array.first] + array.drop(1).map &:to_f
This makes it clear that you want the first element separate from the rest, and you want the rest of the elements to be of type Float. Other options include
array.map { |element, index| index == 0 ? element : element.to_f }
array.map { |element| element == array.first ? element : element.to_f }
You can use a short ternary expression here:
a.map { |e| ( e == a.first ) ? e : e.to_f }
Another option (if you don't want to use ternary operators) is to do the following:
arr = arr.map { |e| (e == arr.first) && e || e.to_f}
This alternative is discussed here. A limitation with this method is that the first element in the array cannot be nil (or some other value that would evaluate false in a boolean evaluation), because if so, it will evaluate to the || expression and return e.to_f instead of just e.
Ruby 1.9 only?
arr = arr.map.with_index { |e, i| i.zero? ? e.to_s : e.to_f }
You can ask the objects themselves whether they're numbers.
"column heading".respond_to?(:to_int) # => false
3.1415926.respond_to?(:to_int) # => true
new_arr = arr.map do |string_or_float|
if string_or_float.respond_to?(:to_int)
string_or_float.to_int # Change from a float into an integer
else
string_or_float # Leave the string as-is
end
end
respond_to?(:to_int) means "Can I call to_int on you?"
to_int is a method that only objects that are readily convertable to integers should have. Unlike to_i, which is "I'm not very much like an integer, but you can try to convert me into a integer", to_int means "I'm very much like an integer - convert me into an integer with full confidence!"
Given an array like : [0,1,1]
How can I elegantly check that: Only one element has a non-zero value and that the others are 0?
(So the above array will fail the check while this array will pass : [1,0,0])
my_array.count(0) == my_array.length-1
If speed is important, for very large arrays where you might need to return early upon detecting a second non-zero, perhaps:
def only_one_non_zero?( array )
found_non_zero = false
array.each do |val|
if val!=0
return false if found_non_zero
found_non_zero = true
end
end
found_non_zero
end
Select at most two non-zero elements, and check if exactly one item was available.
>> [0,1,1].select {|x| !x.zero?}.take(2).size == 1
=> false
>> [0,1,0].select {|x| !x.zero?}.take(2).size == 1
=> true
>> [1,2,3].select {|x| !x.zero?}.take(2).size == 1
=> false
Works fine in Ruby 1.8.7, but note that select returns an array, so it's not "optimally lazy". Here's a blog post showing how to make some lazy enumerators in Ruby.
Thanks for all your answers!
I solved too:
input_array = [0,0,0]
result = input_array - [0]
p result.size == 1 && result[0] == 1
Ruby, I love you!