Как условно посчитать количество элементов в объекте? - PullRequest
0 голосов
/ 18 июня 2020

У меня есть такой объект, как

{
  a: {
    x: 1,
    y: 2
  },
  b: {
    x: 1,
    y: 2
  },
  c: {
    x: 100,
    y: 2
  },
}

Я хотел бы подсчитать количество элементов, которые удовлетворяют условию x: 1. Есть ли простой способ сделать это?

Я мог бы go простым способом, но хотел бы изучить способ JavaScriptoni c (если он есть):

let data = {
  a: {
    x: 1,
    y: 2
  },
  b: {
    x: 1,
    y: 2
  },
  c: {
    x: 100,
    y: 2
  },
}

let counter = 0
for (k in data) {
  if (data[k].x === 1) {
    counter += 1
  }
}

console.log(counter)
// 2

Ответы [ 4 ]

5 голосов
/ 18 июня 2020

Самый чистый способ, который я могу придумать, - это использовать Object.values:

console.log(Object.values(data).filter(v => v.x === 1).length); // 2
1 голос
/ 18 июня 2020

Вы можете использовать функцию .reduce() в сочетании с Object.values(), вот рабочий фрагмент:

let data = {
  a: {
    x: 1,
    y: 2
  },
  b: {
    x: 1,
    y: 2
  },
  c: {
    x: 100,
    y: 2
  },
}
let counter = Object.values(data).reduce((acc,item) => {
  (item.x === 1) ? acc++: 0;
  return acc
}, 0);

console.log(counter)
0 голосов
/ 19 июня 2020

с использованием forEach

let data = {
      a: {
        x: 1,
        y: 2
      },
      b: {
        x: 1,
        y: 2
      },
      c: {
        x: 100,
        y: 2
      },
    }
    let counter = 0
Object.values(data).forEach(item=>{
  item.x===1?counter++:0;
});
  console.log(counter)
0 голосов
/ 19 июня 2020

Как указал Aplet123, вероятно, самый короткий ответ -

Object .values (data) .filter (v => v.x == 1) .length

Хотя это нормально, он создает некоторые ненужные объекты, создавая новый массив объектов, которые совпадают, только чтобы выбросить его после извлечения его количество. Если вы не делаете это очень часто или с огромными наборами данных, это вряд ли проблема, но об этом следует помнить.

Другие альтернативы, либо какой-то вариант вашего исходного 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: Как я уже сказал, урок на другой день.

...