У меня есть, map
который либо меняет значение, либо устанавливает его на ноль. Затем я хочу удалить ноль записей из списка. Список не нужно хранить.
Вот что у меня сейчас есть:
# A simple example function, which returns a value or nil
def transform(n)
rand > 0.5 ? n * 10 : nil }
end
items.map! { |x| transform(x) } # [1, 2, 3, 4, 5] => [10, nil, 30, 40, nil]
items.reject! { |x| x.nil? } # [10, nil, 30, 40, nil] => [10, 30, 40]
Я знаю, что я мог бы просто сделать цикл и условно собрать в другом массиве, как это:
new_items = []
items.each do |x|
x = transform(x)
new_items.append(x) unless x.nil?
end
items = new_items
Но это не кажется идиоматичным. Есть ли хороший способ отобразить функцию на список, удаляя / исключая nils по мере продвижения?
filter_map
, который, кажется, идеально подходит для этого. Сохраняет необходимость повторной обработки массива, вместо этого получая его по желанию с первого раза. Больше информации здесь.Ответы:
Ruby 2.7+
Есть сейчас!
Ruby 2.7 представляет именно
filter_map
для этой цели. Это идиоматично и эффективно, и я ожидаю, что это станет нормой очень скоро.Например:
В вашем случае, поскольку блок оценивается как Falsey, просто:
" Ruby 2.7 добавляет Enumerable # filter_map " - хорошая статья по этому вопросу, с некоторыми оценками производительности по сравнению с некоторыми из более ранних подходов к этой проблеме:
источник
Вы можете использовать
compact
:Я хотел бы напомнить людям, что если вы получаете массив, содержащий nils в качестве вывода
map
блока, и этот блок пытается условно вернуть значения, то у вас есть запах кода и вам нужно переосмыслить свою логику.Например, если вы делаете что-то, что делает это:
Тогда не надо. Вместо этого, до того
map
,reject
что вы не хотите илиselect
что вы хотите:Я считаю использование
compact
уборки беспорядком последней попыткой избавиться от вещей, с которыми мы не справились правильно, обычно потому, что мы не знали, что нас ждет. Мы всегда должны знать, какие данные используются в нашей программе; Неожиданные / неизвестные данные плохие. Каждый раз, когда я вижу nils в массиве, над которым я работаю, я копаюсь, почему они существуют, и вижу, могу ли я улучшить код, генерирующий массив, вместо того, чтобы позволить Ruby тратить время и память на генерацию nils, а затем просеивать через массив, чтобы удалить их позже.источник
nil
записи, а не пустые строки. Кстати,nil
не то же самое, что пустая строка.reduce
илиinject
?compact
это самый быстрый способ, но на самом деле правильное написание кода в начале избавляет от необходимости полностью разбираться с nils.Попробуйте использовать
reduce
илиinject
.Я согласен с принятым ответ , что мы не должны
map
иcompact
, но не по тем же причинам.Я чувствую глубоко внутри, что
map
тогдаcompact
эквивалентноselect
тогдаmap
. Рассмотрим:map
это функция «один к одному». Если вы отображаете из некоторого набора значений и васmap
, то вы хотите одно значение в выходном наборе для каждого значения во входном наборе. Если вам нужно заранееselect
, то вы, вероятно, не хотитеmap
на съемочной площадке. Если вам придетсяselect
потом (илиcompact
), то вы, вероятно, не хотитеmap
на съемочной площадке. В любом случае вы дважды выполняете итерацию по всему набору, когда areduce
нужно пройти только один раз.Кроме того, на английском языке вы пытаетесь «сократить набор целых чисел в набор четных целых чисел».
источник
В вашем примере:
не похоже, что значения изменились, кроме как заменой на
nil
. Если это так, то:будет достаточно.
источник
Если вы хотите, чтобы более слабый критерий отклонения, например, отклонял как пустые строки, так и ноль, вы можете использовать:
Если вы хотите пойти дальше и отклонить нулевые значения (или применить более сложную логику к процессу), вы можете передать блок для отклонения:
источник
blank?
он доступен только в рельсах, мы можем использовать его,items.reject!(&:nil?) # [1, nil, 3, nil, nil] => [1, 3]
который не связан с рельсами. (не исключая пустых строк или нулей)Определенно
compact
это лучший подход для решения этой задачи. Тем не менее, мы можем достичь того же результата просто с помощью простого вычитания:источник
each_with_object
это, наверное, самый чистый путь сюда:На мой взгляд,
each_with_object
это лучше, чемinject
/reduce
в условных случаях, потому что вам не нужно беспокоиться о возвращаемом значении блока.источник
Еще один способ сделать это будет, как показано ниже. Здесь мы используем
Enumerable#each_with_object
для сбора значений и используем,Object#tap
чтобы избавиться от временной переменной, которая в противном случае необходима дляnil
проверки результатаprocess_x
метода.Полный пример для иллюстрации:
Альтернативный подход:
Глядя на метод, который вы вызываете
process_x url
, не ясно, какова цель вводаx
в этот метод. Если я предполагаю, что вы собираетесь обработать значениеx
, передав его несколько,url
и определить, какие из нихx
действительно обрабатываются в допустимые отличные от нуля результаты - тогда, возможно, будетEnumerabble.group_by
лучшим вариантом, чемEnumerable#map
.источник