2013-11-06 2 views
2

Рассмотрите массив Ruby с много элементов.array.select n элементов не более

Я хочу изменить итерацию по массиву и выбрать первые n результатов, для которых данный блок возвращает true.

E.g.

seniors = persons.reverse.select do |person| 
    person.age > 60 
end 

Это нормально, однако он собирает всех пожилых людей, а не n старших в лучшем случае. Каков наилучший способ досрочно прекратить итерацию, когда были собраны n результатов?

+0

является пожилым = seniors.first (п) вариант, вместо преждевременного прекращения? –

+0

Вы можете просто вызвать '.take (n)' после 'end' –

+0

@ user2864740, это неверно. Для массива '[1,2,3,61] .take (3)' будет исключать '61', который должен быть включен в результирующий набор –

ответ

3
seniors = [] 
persons.reverse.each{ |p| 
    seniors << p if p.age > 60 
    break if seniors.count >= n 
} 
+0

Я не думаю, что 'reverse' на самом деле ничего не делает, но это зависит от того, что входной массив равен –

+0

. Это сделает это. Если массив 'seniors' очень велик, я могу также отслеживать его размер в отдельной переменной счетчика, а не называть' .count' на каждой итерации. – jkrmr

+1

Не совсем уверен, но, насколько мне известно, массив Datatype уже отслеживает его счет. –

1

Вы можете цепи take_while и each_with_object:

seniors = [] 
persons.take_while.each_with_object(seniors) do |e, o| 
    o << e if e.age > 60 && o.count < 1000 
    o.count < 1000 
end 

require 'ostruct' 
require 'benchmark' 

persons = 1000000.times.map {|i| OpenStruct.new age: Random.rand(50..85) } 

Benchmark.bm do |b| 
    b.report do 
    seniors = [] 
    persons.take_while.each_with_object(seniors) do |e, o| 
     o << e if e.age > 60 && o.count < 1000 
     o.count < 1000 
    end 
    end 
end 

#=> finishes in 1-3ms on my machine 
2

Lazy evaluation велик, потому что дает возможность писать подобный код без изменения абстракций вы обычно используете в строгих оценках (Haskell доказал, как мощный это есть). К счастью, рубин 2,0 будет поставляться с некоторой инфраструктуры на этом направлении, когда это время наступит, вы будете в состоянии написать это:

# Ruby >= 2.0 
seniors = persons.reverse.lazy.select { |person| person.age > 60 }.take(n) 

рубин < 2.0: https://github.com/yhara/enumerable-lazy

Смежные вопросы