I'm looking to find a way in Ruby to select every nth item in an array. For instance, selecting every second item 开发者_运维问答would transform:
["cat", "dog", "mouse", "tiger"]
into:
["dog", "tiger"]
Is there a Ruby method to do so, or is there any other way to do it?
I tried using something like:
[1,2,3,4].select {|x| x % 2 == 0}
# results in [2,4]
but that only works for an array with integers, not strings.
You can use Enumerable#each_slice
:
["cat", "dog", "mouse", "tiger"].each_slice(2).map(&:last)
# => ["dog", "tiger"]
Update:
As mentioned in the comment, last
is not always suitable, so it could be replaced by first
, and skipping first element:
["cat", "dog", "mouse", "tiger"].drop(1).each_slice(2).map(&:first)
Unfortunately, making it less elegant.
IMO, the most elegant is to use .select.with_index
, which Nakilon suggested in his comment:
["cat", "dog", "mouse", "tiger"].select.with_index{|_,i| (i+1) % 2 == 0}
You could also use step:
n = 2
a = ["cat", "dog", "mouse", "tiger"]
b = (n - 1).step(a.size - 1, n).map { |i| a[i] }
How about this -
arr = ["cat", "dog", "mouse", "tiger"]
n = 2
(0... arr.length).select{ |x| x%n == n-1 }.map { |y| arr[y] }
#=> ["dog", "tiger"]
You can simply use values_at
method. You can find it easily in documentation.
Here are some examples:
array = ["Hello", 2, "apple", 3]
array.values_at(0,1) # pass any number of arguments you like
=> ["Hello", 2]
array.values_at(0..3) # an argument can be a range
=>["Hello", 2, "apple", 3]
I believe this would fix your problem with "dog" and "tiger"
array = ["cat", "dog", "mouse", "tiger"]
array.values_at(1,3)
and with your another array
[1,2,3,4].values_at(1,3)
=> [2, 4]
If you need that in other places, you could add a method to Enumerable
:
module Enumerable
def select_with_index
index = -1
(block_given? && self.class == Range || self.class == Array) ? select { |x| index += 1; yield(x, index) } : self
end
end
p ["cat", "dog", "mouse", "tiger"].select_with_index { |x, i| x if i % 2 != 0 }
Note: This is not my original code. I got it from here when I had had the same need as you.
Yet another ways:
xs.each_with_index.map { |x, idx| x if idx % 2 != 0 }.compact
xs.each_with_index.select { |x, idx| idx % 2 }.map(&:first)
xs.values_at(*(1...xs.length).step(2))
If what you are looking for, is to select only odd or even numbers, there's a very simple way:
animals.select.with_index{ |_, i| i.odd? }
e.g.
['a','b','c','d'].select.with_index{ |_,i| i.odd? }
# => ["b", "d"]
I like both Anshul's and Mu's answers and want to refine and simplify them a bit by submitting each as a monkeypatch to Enumerable:
Mu's
module Enumerable
def every_nth(n)
(n - 1).step(self.size - 1, n).map { |i| self[i] }
end
end
Anshul's
module Enumerable
def every_nth(n)
(0... self.length).select{ |x| x%n == n-1 }.map { |y| self[y] }
end
end
Then it is very easy to work with. For example, consider:
a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
a.every_nth(2)
=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]
a.every_nth(3)
=> [3, 6, 9, 12, 15, 18, 21, 24]
a.every_nth(5)
=> [5, 10, 15, 20, 25]
my_array = ["cat", "dog", "mouse", "tiger"]
my_new_array = my_array.select {|x| index(x) % 2 == 0}
class Array
def every(n)
select {|x| index(x) % n == 0}
end
end
精彩评论