Хотя это нормально, он создает некоторые ненужные объекты, создавая новый массив объектов, которые совпадают, только чтобы выбросить его после извлечения его количество. Если вы не делаете это очень часто или с огромными наборами данных, это вряд ли проблема, но об этом следует помнить.
Другие альтернативы, либо какой-то вариант вашего исходного l oop или Решение на основе reduce
работает нормально, хотя они могут быть немного многословными.
Тем не менее, я бы посоветовал вам подумать об этом с точки зрения повторно используемых частей. Даже такой простой вопрос можно разбить дальше. Представим, что мы пытаемся превратить это упражнение в функцию многократного использования.
Q: Что делает эта предлагаемая функция?
A: Она считает все свойства объекта нашего ввода, которые имеют значение x
1
.
Q: Ну, мы уже сделали это с
(data) => Object .values (data) .filter (v => v.x == 1) .length
Достаточно ли этого?
A: I не понимаю, почему нет. Он делает то, что нам нужно.
Q: Значит, вы думаете, что вам никогда не понадобится найти все те, у которых x
значение 2
?
A: Ну, может быть, но если Да, я могу просто написать новую версию, например
(data) => Object .values (data) .filter (v => v.x == 2) .length.
В этом нет ничего плохого, не так ли?
Q: А затем, если вам нужно x
значение 3
?
A: Тогда я могу просто сделать это снова, верно? Это просто
Q: (непристойный взгляд)
A: Хорошо, я понял! Нам нужно параметризовать это значение. Как насчет того, чтобы написать
(xVal, data) => Object .values (data) .filter (v => v.x == xVal) .length
Q: Это похоже на прогресс?
A: Возможно, но я чувствую, что вы собираетесь что-то спрыгнуть сейчас ...
Q: Я? Никогда!
A: Думаю, я могу догадаться, что будет дальше. Вы спросите, что произойдет, если мне нужно будет сосчитать объекты со значением y
, равным 1
.
Q: Вы учитесь, молодой падаван! Совершенно верно. Итак, как бы вы справились с этим?
A: Я думаю, мы можем просто сделать
(name, val, data) => Object .values (data) .filter (v => v[name] == val) .length
(... ненадолго задумывается ...) Да, это должно сработать.
Q: Итак, теперь, если вам нужно посчитать те, где , оба x
равны 1
и y
равны 3
. Легко ли приспособить это для этой задачи?
A: Это нетривиально. Но я вижу, как передать нашей функции что-то вроде {x: 1, y: 3}
и проверить каждое из свойств этого объекта. Это, безусловно, выполнимо. Вы думаете, что я должен это написать?
Q: А теперь не теряйте терпения! Мы доберемся туда. Это может быть полезная функция. Но что произойдет, если я захочу подсчитать те, у которых свойство x
больше, чем свойство 'y', и оба больше, чем 42
?
A: Тайм-аут! Это очень далеко от первоначального требования. Почему меня должен беспокоить такой случай?
В: Что, если бы я сказал вам, что у вас примерно такое же количество строк кода, что и в вашем первоначальном подходе, мы можем решить все эти проблемы на один раз? Это вас заинтригует?
A: Я полагаю, но вы говорите так, будто мы вообще можем подсчитать совпадающие свойства для любого условия. Звучит безумно.
Q: Это именно то, что я предлагаю. Как мы могли go об этом?
A: Не знаю. Для меня это не имеет особого смысла. Вам нужно будет передать в нашу функцию целую программу. Как вы это делаете?
Q: Ну, возможно, это не программа. Как насчет функции?
A: Полагаю, это тоже подойдет. Но мы не можем волей-неволей передавать функции, не так ли?
Q: Мы сделали это выше.
A: Что вы имеете в виду?
Q: Что мы до сих пор переходили к методу filter
для Array.prototype
в наших ответах?
A: стрелочная функция. Да, я понял, но это для какой-то магической c встроенной части языка. То, что мы можем передать функцию в Array.prototype.filter
, не означает, что мы можем передать ее нашей собственной функции. Как это будет работать?
Q: Давайте попробуем и посмотрим. Если мы собираемся иметь параметр, мы должны назвать его. Как вы думаете, мы должны это назвать?
A: Я знаю. А как насчет magicScrewyThingThatTeacherThinksWillWorkButIStillDoubt
?
Q: Конечно, но вы можете набирать его каждый раз!
A: Хотя серьезно, я не знаю. У вас есть предложения?
Q: Я часто называю такие функции pred
.
A: Сокращение от «хищник»? «Пристрастие»? «Преднизон»? ...
Q: «Предикат». Функция, которая возвращает true
или false
.
A: Хорошо, а затем мы считаем true
s, верно?
Q: Именно так, как бы мы это записали ?
A: Ну, мы бы начали с (data, pred) => Object .values (data) .filter (v =>
... Но что потом?
Q: Ну, тогда мы хотим вызвать нашу функцию, верно? Как мы вызываем функцию?
A: Мы просто используем имя функции, а затем аргументы в круглых скобках. Но у нас нет настоящего имени функции. Все, что у нас есть, это pred
.
Q: Но внутри нашего вызова - это имя функции. Итак, мы можем сделать то же самое. Как это будет выглядеть?
A: Я полагаю, это будет просто
(data, pred) => Object .values (data) .filter (v => pred (v)) .length
Q: И как мы применим это к нашей исходной проблеме?
A: Могу ли я передать стрелочную функцию, как мы, filter
?
Q: Абсолютно.
A: Тогда ... что-то вроде этого?
const countMatchingProps = (data, pred) => Object.values (data) .filter (v => pred (v)) .length
countMatchingProps (data, v => v.x == 1) //=> 2
Q : Это работает?
A: Да! Да, это так. Итак, мы достигли решения?
Q: Нет, еще не совсем. Во-первых, что мы узнали о каррировании?
A: Это снова? Я знаю, как это делать. Я до сих пор не понимаю почему. Мы не делали этого в наших Java классах.
Q: Хорошо, но здесь мы пишем 99% наших функций в каррированной форме. Как бы изменилась эта функция?
A: Итак, мы помещаем сначала параметр, который с наименьшей вероятностью изменится, затем следующий, который с наибольшей вероятностью изменится, и так далее. Для наших двух параметров это означает, что сначала идет предикат, а затем данные. Так как насчет этого?
const countMatchingProps = (pred) => (data) =>
Object.values (data) .filter (v => pred (v)) .length
Это хорошо выглядит?
Q: ????? Вы должны признать, что становится лучше ... Становится намного лучше все время. ????????
A: Ммм ...
Q: Неважно (бормоча «Молодость в наши дни!»). Это намного лучше.
A: «... но»?
Q: Есть одна мелочь. Что делает v => pred(v)
?
A: Это функция. Он принимает значение и возвращает true
или false
.
Q: Точно. А что делает pred
?
A: Это тоже функция. Он принимает значение и возвращает true
или false
. Подождите! Что это значит?
Q: Это означает еще одно упрощение.
A: Могу ли я действительно заменить v => pred(v)
только pred
? Это действительно похоже на обман.
Q: Давайте попробуем и посмотрим.
A:
const countMatchingProps = (pred) => (data) => Object.values (data) .filter (pred) .length
countMatchingProps (v => v.x == 1) (data) //=> 2
Да, работает нормально. Но я не уверен, что хочу включить эту стрелочную функцию ...
Q: помните, что ...
A: Я знаю, я знаю "помните, что мы предпочитаем звонить стрелочные функции лямбды ". Я до сих пор не знаю почему. В любом случае, я не уверен, что хочу включать эту лямбда каждый раз, когда мне нужно подсчитывать те, у которых свойство x
равно 1
. Могу ли я сделать из этого специфику c функцию для моего варианта использования?
Q: Вот для чего должны были быть уроки каррирования. Разве вы не помните, как мы это делаем?
A: Это ... частичное применение? О каррированной функции?
Q: (кивает)
A: Итак, я могу просто написать
const countX1s = countMatchingProps (v => v.x == 1)
// ... later
countX1s (data) //=> 2
О, это имеет смысл. Почему вы просто не сказали это, когда мы обсуждали карри?
Q: (раздраженный вздох)
A: Хорошо, наконец-то мы пришли к нашему ответу!
Q: Ну ...
A: О нет! Подробнее?
Q: Еще один шаг. Что, если бы мы хотели сделать что-то подобное для массивов? Подсчитать все те, которые соответствуют какому-либо условию?
A: Ну, после этого все просто:
const countMatches = (pred) => (arr) => arr .filter (pred) .length
Q: Вы видите сходство между countMatches
и countMatchingProps
?
A: Ну да, это как если бы countMatches
встроено в countMatchingProps
.
Q: Совершенно верно. Так что же нам делать в таких случаях?
A: Мы проводим рефакторинг.
Q: Совершенно верно. Как это будет выглядеть?
A: Я думаю, это достаточно просто:
const countMatches = (pred) => (arr) => arr .filter (pred) .length
const countMatchingProps = (pred) => (arr) => countMatches (pred) (Object .values (arr))
const countX1s = countMatchingProps (v => v.x == 1)
// ... later
countX1s (data) //=> 2
Q: А как вы относитесь к этому коду?
A: Ну, мы началось с простого требования, и то, что мы написали, намного, намного мощнее. Это должно быть хорошо.
Q: А код стал намного сложнее?
A: Ну, по сравнению с моей первоначальной for
-l oop версией, вероятно, минус 1259 * комплекс. Но она все же сложнее, чем версия от Aplet123.
Q: Да, абстракция всегда имеет свою цену. Но как вам эта версия кода?
A: Я все еще поглощаю ее. Но я думаю, мне это очень нравится.
Q: Нет, но это урок на другой день.
A: Давай, после того, как ты представил мне свое исполнение Битлз? Я думаю, ты мне должен.
Q: Так ты узнал песню? Возможно, у сегодняшней молодежи еще есть надежда ... Хорошо. Моя версия может выглядеть так:
const count = compose (length, filter)
const countProps = compose2 (count, identity, values)
const countX1s = countProps (propEq ('x', 1))
A: А?
Q: Как я уже сказал, урок на другой день.