I have an array in Ruby 2.0:
arr=[1,2,3,4,5]
I would like to be able to do something like:
arr[6] #=> 2
That is - roll over the end of the array and restart. Is that possible?
This would work:
arr = [1,2,3,4,5]
arr[6 % arr.size] #=> 2
Yes, it is possible to patch Array class in such way:
module RollOver
def [](index)
super index % size
end
end
Array.class_eval do
prepend RollOver
end
array = [1, 2, 3, 4, 5]
puts array[6] # => 2
But it is NOT recommended. Imagine how much code could be broken because of this patch. You better define another method for such operation.
UPDATE
If this behaviour is needed for one particular array only, then the best solution is:
array = [1, 2, 3, 4, 5]
def array.[](index)
super index % size
end
puts array[6] # => 2
Yes, Ruby allows that :-)
Related
I was wondering if there is a more idiomatic way to get the functionality represented by the code below. Basically I just want to check if the array contains the elements in pattern in the order specified by pattern. It's okay for there to be gaps between these elements.
class Array
def has_pattern?(pattern)
offset = 0
pattern.each do |p|
offset = self[offset..-1].index(p)
return false if offset.nil?
end
return true
end
end
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 4, 5]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([2, 3, 1]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 3, 2]) # false
The code above seems to work, but doesn't feel like idiomatic Ruby to me. Is there a nicer way to write this?
Here's my take on it:
class Array
def has_pattern?(ptn)
i = 0
self.each do |elem|
i += 1 if elem == ptn[i]
end
i >= ptn.size
end
end
It passes through the array only once, so it may make a difference when the array's big.
Here's a different way to approach it:
class Array
def has_pattern?(pattern)
(self - (self - pattern))
.each_cons(pattern.length)
.any? { |p| p === pattern }
end
end
But, as I said in the comments above, I think your solution is superior.
I think infinite enumerator is very convenient for writing FP style scripts but I have yet to find a comfortable way to construct such structure in Ruby.
I know I can construct it explicitly:
a = Enumerator.new do |y|
i = 0
loop do
y << i += 1
end
end
a.next #=> 1
a.next #=> 2
a.next #=> 3
...
but that's annoyingly wordy for such a simple structure.
Another approach is sort of a "hack" of using Float::INFINITY:
b = (1..Float::INFINITY).each
b = (1..1.0/0.0).each
These two are probably the least clumsy solution I can give. Although I'd like to know if there are some other more elegant way of constructing infinite enumerators. (By the way, why doesn't Ruby just make inf or infinity as a literal for Float::INFINITY?)
Use #to_enum or #lazy to convert your Range to an Enumerable. For example:
(1..Float::INFINITY).to_enum
(1..Float::INFINITY).lazy
I would personally create my own Ruby class for this.
class NaturalNumbers
def self.each
i = 0
loop { yield i += 1 }
end
end
NaturalNumbers.each do |i|
puts i
end
Ruby 2.7 introduced Enumerator#produce for creating an infinite enumerator from any block, which results in a very elegant, very functional way of implementing the original problem:
irb(main):001:0> NaturalNumbers = Enumerator.produce(0) { |x| x + 1 }
=> #<Enumerator: #<Enumerator::Producer:0x00007fadbd82d990>:each>
irb(main):002:0> NaturalNumbers.first(10)
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):003:0> _
... which - if you're a fan of numbered block parameters (another Ruby 2.7 feature) - can also be written as:
irb(main):006:0> NaturalNumbers = Enumerator.produce(0) { _1 + 1 }
=> #<Enumerator: #<Enumerator::Producer:0x00007fadbc8b08f0>:each>
irb(main):007:0> NaturalNumbers.first(10)
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):008:0> _
def peel array
output = []
while ! array.empty? do
output << array.shift
mutate! array
end
output.flatten
end
I have not included the mutate! method, because I am only interested in removing the output variable. The mutate! call is important because we cannot iterate over the array using each because array is changing.
EDIT: I am getting an array as output, which is what I want. The method works correctly, but I think there is a way to collect the array.shift values without using a temp variable.
EDIT #2: OK, here is the mutate! method and test case:
def mutate! array
array.reverse!
end
a = (1..5).to_a
peel( a ).should == [ 1, 5, 2, 4, 3 ]
It doesn't matter if peel modifies array. I guess it should be called peel!. Yes, mutate! must be called after each element is removed.
All this reversing makes me dizzy.
def peel(array)
indices = array.size.times.map do |i|
i = -i if i.odd?
i = i/2
end
array.values_at(*indices) # indices will be [0, -1, 1, -2, 2] in the example
end
a = (1..5).to_a
p peel(a) #=>[1, 5, 2, 4, 3]
Another approach:
def peel(array)
mid = array.size/2
array[0..mid]
.zip(array[mid..-1].reverse)
.flatten(1)
.take(array.size)
end
Usage:
peel [1,2,3,4,5,6]
#=> [1, 6, 2, 5, 3, 4]
peel [1,2,3,4,5]
#=> [1, 5, 2, 4, 3]
Here's a way using parallel assignment:
def peel array
n = array.size
n.times {|i| (n-2-2*i).times {|j| array[n-1-j], array[n-2-j] = array[n-2-j], array[n-1-j]}}
array
end
peel [1,2,3,4,5] # => [1,5,2,4,3]
peel [1,2,3,4,5,6] # => [1,6,2,5,3,4]
What I'm doing here is a series of pairwise exchanges. By way of example, for [1,2,3,4,5,6], the first 6-2=4 steps (6 being the size of the array) alter the array as follows:
[1,2,3,4,6,5]
[1,2,3,6,4,5]
[1,2,6,3,4,5]
[1,6,2,3,4,5]
The 1, 6 and the 2 are in now the right positions. We repeat these steps, but this time only 6-4=2 times, to move the 5 and 3 into the correct positions:
[1,6,2,3,5,4]
[1,6,2,5,3,4]
The 4 is pushed to the end, it's correct position, so we are finished.
I try to clean my Code. The first Version uses each_with_index. In the second version I tried to compact the code with the Enumerable.inject_with_index-construct, that I found here.
It works now, but seems to me as obscure as the first code.
Add even worse I don't understand the brackets around element,index in
.. .inject(groups) do |group_container, (element,index)|
but they are necessary
What is the use of these brackets?
How can I make the code clear and readable?
FIRST VERSION -- WITH "each_with_index"
class Array
# splits as good as possible to groups of same size
# elements are sorted. I.e. low elements go to the first group,
# and high elements to the last group
#
# the default for number_of_groups is 4
# because the intended use case is
# splitting statistic data in 4 quartiles
#
# a = [1, 8, 7, 5, 4, 2, 3, 8]
# a.sorted_in_groups(3) # => [[1, 2, 3], [4, 5, 7], [8, 8]]
#
# b = [[7, 8, 9], [4, 5, 7], [2, 8]]
# b.sorted_in_groups(2) {|sub_ary| sub_ary.sum } # => [ [[2, 8], [4, 5, 7]], [[7, 8, 9]] ]
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index do |element, index|
group_number = (index.to_f / average_group_size).floor
groups[group_number] << element
end
groups
end
end
SECOND VERSION -- WITH "inject" AND index
class Array
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index.inject(groups) do |group_container, (element,index)|
group_number = (index.to_f / average_group_size).floor
group_container[group_number] << element
group_container
end
end
end
What is the use of these brackets?
It's a very nice feature of ruby. I call it "destructuring array assignment", but it probably has an official name too.
Here's how it works. Let's say you have an array
arr = [1, 2, 3]
Then you assign this array to a list of names, like this:
a, b, c = arr
a # => 1
b # => 2
c # => 3
You see, the array was "destructured" into its individual elements. Now, to the each_with_index. As you know, it's like a regular each, but also returns an index. inject doesn't care about all this, it takes input elements and passes them to its block as is. If input element is an array (elem/index pair from each_with_index), then we can either take it apart in the block body
sorted.each_with_index.inject(groups) do |group_container, pair|
element, index = pair
# or
# element = pair[0]
# index = pair[1]
# rest of your code
end
Or destructure that array right in the block signature. Parentheses there are necessary to give ruby a hint that this is a single parameter that needs to be split in several.
Hope this helps.
lines = %w(a b c)
indexes = lines.each_with_index.inject([]) do |acc, (el, ind)|
acc << ind - 1 if el == "b"
acc
end
indexes # => [0]
What is the use of these brackets?
To understand the brackets, first you need to understand how destruction works in ruby. The simplest example I can think of this this:
1.8.7 :001 > [[1,3],[2,4]].each do |a,b|
1.8.7 :002 > puts a, b
1.8.7 :003?> end
1
3
2
4
You should know how each function works, and that the block receives one parameter. So what happens when you pass two parameters? It takes the first element [1,3] and try to split (destruct) it in two, and the result is a=1 and b=3.
Now, inject takes two arguments in the block parameter, so it is usually looks like |a,b|. So passing a parameter like |group_container, (element,index)| we are in fact taking the first one as any other, and destructing the second in two others (so, if the second parameter is [1,3], element=1 and index=3). The parenthesis are needed because if we used |group_container, element, index| we would never know if we are destructing the first or the second parameter, so the parenthesis there works as disambiguation.
9In fact, things works a bit different in the bottom end, but lets hide this for this given question.)
Seems like there already some answers given with good explanation. I want to add some information regards the clear and readable.
Instead of the solution you chose, it is also a possibility to extend Enumerable and add this functionality.
module Enumerable
# The block parameter is not needed but creates more readable code.
def inject_with_index(memo = self.first, &block)
skip = memo.equal?(self.first)
index = 0
self.each_entry do |entry|
if skip
skip = false
else
memo = yield(memo, index, entry)
end
index += 1
end
memo
end
end
This way you can call inject_with_index like so:
# m = memo, i = index, e = entry
(1..3).inject_with_index(0) do |m, i, e|
puts "m: #{m}, i: #{i}, e: #{e}"
m + i + e
end
#=> 9
If you not pass an initial value the first element will be used, thus not executing the block for the first element.
In case, someone is here from 2013+ year, you have each_with_object and with_index for your needs:
records.each_with_object({}).with_index do |(record, memo), index|
memo[record.uid] = "#{index} in collection}"
end
I have an array that I want to iterate over and delete some of the elements. This doesn't work:
a = [1, 2, 3, 4, 5]
a.each do |x|
next if x < 3
a.delete x
# do something with x
end
a #=> [1, 2, 4]
I want a to be [1, 2]. How can I get around this?
a.delete_if { |x| x >= 3 }
See method documentation here
Update:
You can handle x in the block:
a.delete_if do |element|
if element >= 3
do_something_with(element)
true # Make sure the if statement returns true, so it gets marked for deletion
end
end
You don't have to delete from the array, you can filter it so:
a = [1, 2, 3, 4, 5]
b = a.select {|x| x < 3}
puts b.inspect # => [1,2]
b.each {|i| puts i} # do something to each here
I asked this question not long ago.
Deleting While Iterating in Ruby?
It's not working because Ruby exits the .each loop when attempting to delete something. If you simply want to delete things from the array, delete_if will work, but if you want more control, the solution I have in that thread works, though it's kind of ugly.
Another way to do it is using reject!, which is arguably clearer since it has a ! which means "this will change the array". The only difference is that reject! will return nil if no changes were made.
a.delete_if {|x| x >= 3 }
or
a.reject! {|x| x >= 3 }
will both work fine.