Ruby: bsearch returning nil - ruby

I have an sorted array of number, and wanted to check if number is in the array or not. I think I should use bsearch here but it return nil even if the number is present in the array, and only give positive result for the middle element.
Here is what I test.
>> [1,2,3,4,5].bsearch { |value| value <=> 1}
>> nil
>> [1,2,3,4,5].bsearch { |value| value <=> 3}
>> 3
I am using Ruby version 2.3.8

You are using Array#bsearch in Find-any mode.
As the documentation says, your block needs to satisfy the following requirements:
All positive-evaluating elements precede all zero-evaluating elements.
All positive-evaluating elements precede all negative-evaluating elements.
All zero-evaluating elements precede all negative-evaluating elements.
Your block violates those requirements.
Let's run through an example. In the first iteration, Array#bsearch will pick the middle element, which is 3 and pass it to your block. Your block returns 1, which means that the element you are searching for is greater than 3 and thus must be to the right of the current element. So, Array#bsearch throws away the left half of the array, and we are left with [4, 5]. Now again, Array#bsearch picks the "middle" element, which is 4 and passes it to your block. Your block returns 1, which is a positive number and thus means that the element you are looking for is greater than 4 and thus must be in the right half of the array. So again, Array#bsearch throws away the left half and we are left with [5]. Same thing, Array#bsearch passes 5 to the block, your block returns 1 which is a positive number and thus means we need to look to the right, except we are already at the end of the array, and thus we can conclude that the element you are looking for is not in the array.
So, basically, you are just telling Array#bsearch to look in the wrong place.
Let's look again at the documentation:
These make sense as blocks in find-any mode:
a = [0, 4, 7, 10, 12]
a.map {|element| 7 <=> element } # => [1, 1, 0, -1, -1]
a.map {|element| -1 <=> element } # => [-1, -1, -1, -1, -1]
a.map {|element| 5 <=> element } # => [1, 1, -1, -1, -1]
a.map {|element| 15 <=> element } # => [1, 1, 1, 1, 1]
This would not make sense:
a = [0, 4, 7, 10, 12]
a.map {|element| element <=> 7 } # => [-1, -1, 0, 1, 1]
So, essentially you wrote your block exactly like the block that the documentation explicitly says is wrong, and exactly the opposite way of what the documentation tells you to.
If you flip the two operands in the block, so that your block looks like the example in the documentation says it should look, it will work:
[1, 2, 3, 4, 5].bsearch { |value| 0 <=> value } #=> nil
[1, 2, 3, 4, 5].bsearch { |value| 1 <=> value } #=> 1
[1, 2, 3, 4, 5].bsearch { |value| 2 <=> value } #=> 2
[1, 2, 3, 4, 5].bsearch { |value| 3 <=> value } #=> 3
[1, 2, 3, 4, 5].bsearch { |value| 4 <=> value } #=> 4
[1, 2, 3, 4, 5].bsearch { |value| 5 <=> value } #=> 5
[1, 2, 3, 4, 5].bsearch { |value| 6 <=> value } #=> nil

Related

What does Ruby block return to?

Good day. I've tried writing this code in ruby
x = [1, 2, 3, 4, 5]
x.each do |a|
a + 1
end
When I type this in irb, I don't understand why does it return
=> [1, 2, 3, 4, 5]
I thought it would return
=> [2, 3, 4, 5, 6] # because of a + 1
each yields the array's elements to the given block (one after another) without modifying the array. At the end, it returns the array, as mentioned in the docs:
[...] passes each successive array element to the block; returns self
You are probably looking for map, which works similar to each but instead of returning self, it ...
[...] returns a new Array whose elements are the return values from the block
Example:
x = [1, 2, 3, 4, 5]
x.map { |a| a + 1 }
#=> [2, 3, 4, 5, 6]
Note that it returns a new array without actually modifying x. There's also map! (with !) which does modify the receiver.

How to select the first n elements from Ruby array that satisfy a predicate?

I want to get all items from an array, which satisfy a predicate. Once I see an element that doesn't satisfy, I should stop iterating. For example:
[1, 4, -9, 3, 6].select_only_first { |x| x > 0}
I'm expecting to get: [1, 4]
This is how you want :
arup#linux-wzza:~> pry
[1] pry(main)> [1, 4, -9, 3, 6].take_while { |x| x > 0}
=> [1, 4]
[2] pry(main)>
Here is the documentation :
arup#linux-wzza:~> ri Array#take_while
= Array#take_while
(from ruby site)
------------------------------------------------------------------------------
ary.take_while { |arr| block } -> new_ary
ary.take_while -> Enumerator
------------------------------------------------------------------------------
Passes elements to the block until the block returns nil or false, then stops
iterating and returns an array of all prior elements.
If no block is given, an Enumerator is returned instead.
See also Array#drop_while
a = [1, 2, 3, 4, 5, 0]
a.take_while { |i| i < 3 } #=> [1, 2]
lines 1-20/20 (END)
If you're exploring other solution, this works too:
[1, 4, -9, 3, 6].slice_before { |x| x <= 0}.to_a[0]
You have to change x > 0 to x <=0.
That was an excellent answer Arup. My method is slightly more complicated.
numbers = [1,4,-9,3,6]
i = 0
new_numbers = []
until numbers[i] < 0
new_numbers.push(numbers[i])
i+= 1
end
=> [1,4]

How to take one element out of an array and put in front?

a = [1,2,3]
b = [2,1,3]
What is the best way to get b from a?
My inelegant solution:
x = 2
y = a - [x]
b = y.unshift(x)
a.unshift a.delete(2)
This appends the recently deleted object (here 2).
Beware that, if the object in question appears more than once in the array, all occurences will be deleted.
In case you want only the first occurrence of an object to be moved, try this:
a = [1,2,3,2]
a.unshift a.delete_at(a.index(2))
# => [2, 1, 3, 2]
a.unshift a.slice!(a.index(2)||0)
# => [2, 1, 3]
If there are multiple instances, only the first instance is moved to the front.
If the element doesn't exist, then a is unchanged.
If you wanted to move elements of an array arbitrarily, you could do something like this:
Code
# Return a copy of the receiver array, with the receiver's element at
# offset i moved before the element at offset j, unless j == self.size,
# in which case the element at offset i is moved to the end of the array.
class Array
def move(i,j)
a = dup
case
when i < 0 || i >= size
raise ArgumentError, "From index is out-of-range"
when j < 0 || j > size
raise ArgumentError, "To index is out-of-range"
when j < i
a.insert(j, a.delete_at(i))
when j == size
a << a.delete_at(i)
when j > i+1
a.insert(j-1, a.delete_at(i))
else
a
end
end
end
With Ruby v2.1, you could optionally replace class Array with refine Array. (Module#refine was introduced experimentally in v2.0, but was changed substantially in v2.1.)
Demo
arr = [1,2,3,4,5] #=> [1, 2, 3, 4, 5]
arr.move(2,1) #=> [1, 3, 2, 4, 5]
arr.move(2,2) #=> [1, 2, 3, 4, 5]
arr.move(2,3) #=> [1, 2, 3, 4, 5]
arr.move(2,4) #=> [1, 2, 4, 3, 5]
arr.move(2,5) #=> [1, 2, 4, 5, 3]
arr.move(2,6) #=> ArgumentError: To index is out-of-range

How do I pick a fractional sample from an array?

I know that ruby has myarray.sample(i) to sample i elements from an array. My problem is that the number of elements are not integers.
i.e I would like a method mysample such that if I call myarray.mysample(1.5) 10 times, the number of elements I get should be close to 15.
With sample, I will get either 10 or 20 depending on the int conversion. Similarly, if I call myarray.mysample(.25) I want it to return an element with 0.25 probability (that is, it should return one element one times out of four, and three times out of four, it should return an empty array/nil).
How do I do this?
My attempt so far:
def mysample(array,s)
ints = array.sample(s.floor)
if (Random.rand > s - s.floor)
ints << array.sample
end
return ints
end
Is there a better way?
Basing my answer off of this:
if I call myarray.mysample(1.5) 10 times, the number of elements I get should be close to 15.
Extending Array yields the following:
class Array
def mysample(num)
self.sample( ( num + rand() ).floor )
end
end
> [1, 2, 3, 4, 5].mysample(2.5)
=> [1, 3]
> [1, 2, 3, 4, 5].mysample(2.5)
=> [4, 2, 5]
> [1, 2, 3, 4, 5].mysample(0.5)
=> []
> [1, 2, 3, 4, 5].mysample(0.5)
=> [3]
etc.
To optimal argument is there to decide the spread of randomness for numbers above 1.
class Array
def my_sample(number, deviation=0.3)
if number < 1
return sample rand(100) < number * 100 ? 1 : 0
end
speard = (number*deviation).to_i
randomness = rand(-speard..speard)
sample(number+randomness)
end
end
p [1,2,3,4,5,6,7,8,9,10].my_sample(0.5) #=> []
p [1,2,3,4,5,6,7,8,9,10].my_sample(0.5) #=> [3]
p [1,2,3,4,5,6,7,8,9,10].my_sample(5) #=> [9, 2, 1, 4, 10, 7, 3]
p [1,2,3,4,5,6,7,8,9,10].my_sample(5) #=> [7, 2, 3, 8]

Enumerators in Ruby

I'm having a trouble understanding Enumerators in Ruby.
Please correct me If I'm wrong, o.enum_for(:arg) method is supposed to convert object to Enumerator and every iteration over object o should call arg method?
What confuses me is how this line of code works
[4, 1, 2, 0].enum_for(:count).each_with_index do |elem, index|
elem == index
end
It should count how many elements are equal to their position in the array, and it works. However, I don't understand what's actually going on. Is each_with_index calling count method on every iteration? If someone could explain, it would be great.
From Programming Ruby:
count enum.count {| obj | block } → int
Returns the count of objects in enum that equal obj or for which the
block returns a true value.
So the enumerator visits each element and adds 1 to the count if the block returns true, which in this case means if the element is equal to the array index.
I haven't seen this pattern used much (if at all)—I would be more inclined to:
[4, 1, 2, 0].each_with_index.select { |elem, index| elem == index }.count
EDIT
Lets take a look at the example from your comment:
[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a, b|
a + b
end
each_slice(2) takes the array 2 elements at a time and returns an array for each slice:
[4, 1, 2, 0].each_slice(2).map # => [[4,1], [2,0]]
calling map on the result lets us operate on each sub-array, passing it into a block:
[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a,b|
puts "#{a.inspect} #{b.inspect}"
end
results in
4 1
2 0
a and b get their values by virtue of the block arguments being "splatted":
a, b = *[4, 1]
a # => 4
b # => 1
You could also take the array slice as the argument instead:
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| puts "#{a.inspect}"}
[4, 1]
[2, 0]
Which lets you do this:
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.inject(:+) } #=> [5,2]
Or if you have ActiveSupport (i.e. a Rails app),
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.sum }
Which seems a lot clearer to me than the original example.
array.count can normally take a block, but on its own, it returns a fixnum, so it can't be chained to .with_index the way some other iterators can (try array.map.with_index {|x,i ... }, etc).
.enum_for(:count) converts it into a enumerator, which allows that chaining to take place. It iterates once over the members of array, and keeps a tally of how many of them equal their indexes. So count is really only being called once, but only after converting the array into something more flexible.

Resources