Наиболее эффективный способ поиска среднего уровня использования lodash

У меня есть массив объектов, количество объектов - переменное -

var people = [{
  name: john,
  job: manager,
  salary: 2000
},
  {
  name: sam,
  job: manager,
  salary: 6000
},
  {
  name: frodo,
  job: janitor
}];

Какой самый элегантный способ найти среднюю зарплату всех менеджеров, использующих lodash? (Я предполагаю, что мы должны проверить, является ли объект менеджером, а также если объект имеет свойство зарплаты)

Я думал в нижних строках -

_(people).filter(function(name) {
    return name.occupation === "manager" && _(name).has("salary");}).pluck("salary").reduce(function(sum,num) { return sum+num });

Но я не уверен, что это правильный подход.

Ответ 1

"эффективный" - очень двусмысленный термин. Говоря "эффективно", вы можете думать о производительности, читаемости или краткости и т.д. Я думаю, что наиболее читаемым и лаконичным решением является:

_(people).filter({ job: 'manager'}).filter('salary').reduce(function(a,m,i,p) {
    return a + m.salary/p.length;
},0);

Самое быстрое решение - не использовать loadash, ни библиотеку, ни какие-либо методы filter, reduce. Вместо этого используйте for:

var sum    = 0;
var count  = 0;
for (var i = 0, ii = people.length; i < ii; ++i) {
    var man = people[i];

    if (typeof man.salary !== 'undefined') {
        sum += man.salary;
        ++count;
    }
}
var avg = sum/count;

Я думаю, что для чтения на стороне клиента читаемость важнее производительности в большинстве случаев, поэтому я считаю, что первый вариант наиболее эффективен.

Ответ 2

Почему все люди переусердствуют здесь?

const people = [
 {name: 'Alejandro', budget: 56},
 {name: 'Juan', budget: 86},
 {name: 'Pedro', budget: 99}];

let average = _.meanBy(people, (p) => p.budget);

В соответствии с документами: https://lodash.com/docs/#meanBy

Ответ 3

Я не знаю о lowdash, но, возможно, простое решение JS поможет вам добраться:

console.log(people.reduce(function(values, obj) {
              if (obj.hasOwnProperty('salary')) {
                values.sum += obj.salary;
                values.count++;
                values.average = values.sum / values.count;
              }
              return values;
            }, {sum:0, count:0, average: void 0}).average
); // 4000

Это передает объект для уменьшения как аккумулятора, который имеет три свойства: сумму зарплат, количество зарплат и среднее значение. Он выполняет итерацию по всем объектам, суммируя зарплаты, подсчитывая, сколько их есть и вычисляет среднее значение на каждой итерации. В конце концов он возвращает этот объект (аккумулятор) и считывает среднее свойство.

Вызов одного встроенного метода должен быть более быстрым (то есть более эффективным), чем вызов 4 собственных функций. "Элегантный" находится в глазах смотрящего.; -)

Кстати, в объектном литерале есть ошибки, это должно быть:

var people = [{
  name: 'john',
  job: 'manager',
  salary: 2000
},
  {
  name: 'sam',
  job: 'manager',
  salary: 6000
},
  {
  name: 'frodo',
  job: 'janitor'
}];

Ответ 4

function average(acc, ele, index) {
    return (acc + ele) / (index + 1);
}

var result = _.chain(people)
    .filter('job', 'manager')
    .map('salary')
    .reduce( average )
    .value();

Ответ 5

С более функциональной версией lodash (lodash-fp) и es2015 вы можете использовать функции стрелок и автокарри, чтобы получить более гибкое и функциональное ароматизированное решение.

Вы можете поместить его в уродливый один лайнер:

const result = _.flow(_.filter(['job', 'manager']), 
    e => _.sumBy('salary', e) / _.countBy(_.has('salary'), e).true)(people);

Или вы можете создать аккуратный DSL:

const hasSalary = _.has('salary');
const countWhenHasSalary = _.countBy(hasSalary);
const salarySum = _.sumBy('salary');
const salaryAvg = a => salarySum(a) / countWhenHasSalary(a).true;
const filterByJob = job => _.filter(['job', job]);
const salaryAvgByJob = job => _.flow(filterByJob(job), salaryAvg);

const result = salaryAvgByJob('manager')(people);

Ответ 6

Самый чистый (элегантный) способ, о котором я мог думать, был:

var salariesOfManagers = _(people).filter({job: 'manager'}).filter('salary').pluck('salary');
var averageSalary = salariesOfManagers.sum() / salariesOfManagers.value().length;

Требуется сумма элементов и делит ее на количество элементов, что в значительной степени определяется средним значением.

Слишком плохо, что если вы хотите сделать это аккуратным однострочным, код станет менее понятным для чтения.

Ответ 7

Используя lodash/fp и ES2016/ES6, это можно сделать более функциональным способом

const avg = flow(
    filter({job: 'manager'}), 
    map('salary'),
    mean
)

console.log(avg(people))

Что вы делаете 1. Получить тип всех менеджеров объектов 2. Извлеките из них свойство/поле "заработная плата" 3. Найти среднее значение с помощью средней функции

Вот полная версия кода для вашего удобства, которая выполняется на узлах.

'use strict'
const _ = require('lodash/fp');

const {
    flow,
    filter,
    map,
    mean
} = _

const people = [{
    name: 'john',
    job: 'manager',
    salary: 2000
}, {
    name: 'sam',
    job: 'manager',
    salary: 6000
}, {
    name: 'frodo',
    job: 'janitor'
}];

const avg = flow(
    filter({job: 'manager'}), 
    map('salary'),
    mean
)

console.log(avg(people))

Ответ 8

lodash v3:

_.sum(people, 'salary') / people.length (people не должно быть пустым)

lodash v4:

_.meanBy(people, 'salary')