如何在 UnderscoreJS 中重构对象并求和

How to restructure an object and find sum in UnderscoreJS

我需要对每个班次(S1、S2、S3)的机器(M1、M2、M3...)的键值(K1、K2、K3...)求和并创建一个新对象(给定下)

我对 jquery $.eachforEach 方法失去了耐心(因为我需要处理 5 个级别的对象)并遇到了 UnderscoreJS。

我知道我应该为对象使用多种函数组合 http://underscorejs.org/#objects 但是由于我是一个纯粹的前端人员,对查询和数据库知之甚少,所以我找不到正确的方法那。

给定 JSON 结构:

var data = {
    "28-11":{
        "S1":{
            "M1":{
                "K1": 10,
                "K2": 12,
                "K3": 15
            },
            "M2":{
                "K1": 8,
                "K2": 6,
                "K3": 5
            }
        },
        "S2":{
            "M1":{
                "K1": 8,
                "K2": 6,
                "K3": 5
            },
            "M2":{
                "K1": 10,
                "K2": 12,
                "K3": 15
            }
        }
    }
}

我需要获得以下信息:

var allShiftsData = {
  "28-11":{
    "M1":{
      "K1": 18,
      "K2": 18,
      "K3": 20
    },
    "M2":{
      "K1": 18,
      "K2": 18,
      "K3": 20
    }
  }
}

您可以使用 Object.keys 和 Array.forEach 来实现您的需要。

var data = {
  "28-11": {
    "S1": {
      "M1": {
        "K1": 10,
        "K2": 12,
        "K3": 15
      },
      "M2": {
        "K1": 8,
        "K2": 6,
        "K3": 5
      }
    },
    "S2": {
      "M1": {
        "K1": 8,
        "K2": 6,
        "K3": 5
      },
      "M2": {
        "K1": 10,
        "K2": 12,
        "K3": 15
      }
    }
  }
};

var output = {};

//Iterate over Dates
Object.keys(data).forEach((date) => {
  var dateObj = data[date];
  output[date] = {};

  //Iterate over Shifts
  Object.keys(dateObj).forEach((shift) => {
    var shiftObj = dateObj[shift];

    //Iterate over machines
    Object.keys(shiftObj).forEach((machine) => {
      //Initialize if the machine is not already iterated earlier.
      output[date][machine] = output[date][machine] || {};
      Object.keys(shiftObj[machine]).forEach((keyValue) => {

        if (!output[date][machine][keyValue]) {
          output[date][machine][keyValue] = 0;
        }

        output[date][machine][keyValue] += shiftObj[machine][keyValue];

      });
    });
  });
});


console.log(output);

解决这个问题的一种方法是对每个对象属性进行 4 级迭代,当我们遇到它们时将机器求和到结果中:

const data = {"28-11":{"S1":{"M1":{"K1":10,"K2":12,"K3":15},"M2":{"K1":8,"K2":6,"K3":5}},"S2":{"M1":{"K1":8,"K2":6,"K3":5},"M2":{"K1":10,"K2":12,"K3":15}}}};

const {
  keys
} = Object;

const each = (obj, cb) => {
  for (const prop of keys(obj)) {
    cb(obj[prop], prop);
  }
};

const sumShiftsPerDate = (data) => {
  const result = {};
  
  each(data, (shifts, date) => {
    result[date] = {};

    each(shifts, (machines, shift) => {
      each(machines, (machineKeys, machine) => {
        result[date][machine] = result[date][machine] || {};

        each(machineKeys, (keyValue, keyName) => {
          result[date][machine][keyName] = 
            (result[date][machine][keyName] || 0) + keyValue;
        });
      });
    });
  });

  return result;
};

console.log(sumShiftsPerDate(data));

或使用下划线的等价物 each:

const data = {"28-11":{"S1":{"M1":{"K1":10,"K2":12,"K3":15},"M2":{"K1":8,"K2":6,"K3":5}},"S2":{"M1":{"K1":8,"K2":6,"K3":5},"M2":{"K1":10,"K2":12,"K3":15}}}};

const sumShiftsPerDate = (data) => {
  const result = {};
  
  _.each(data, (shifts, date) => {
    result[date] = {};

    _.each(shifts, (machines, shift) => {
      _.each(machines, (machineKeys, machine) => {
        result[date][machine] = result[date][machine] || {};

        _.each(machineKeys, (keyValue, keyName) => {
          result[date][machine][keyName] = 
            (result[date][machine][keyName] || 0) + keyValue;
        });
      });
    });
  });

  return result;
};

console.log(sumShiftsPerDate(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

一种更实用的方法可能是使用对象映射将每个日期对象从班次容器转换为机器容器,并使用轮班归约将它们转换为机器键值的总和。

const data = {"28-11":{"S1":{"M1":{"K1":10,"K2":12,"K3":15},"M2":{"K1":8,"K2":6,"K3":5}},"S2":{"M1":{"K1":8,"K2":6,"K3":5},"M2":{"K1":10,"K2":12,"K3":15}}}};

const {
  assign, keys
} = Object;

const reduceObject = (obj, cb, initial) => keys(obj).
  reduce((acc, prop) => cb(acc, obj[prop], prop), initial);

const mapObject = (obj, cb) => reduceObject(obj, (result, val, prop) =>
  assign(result, {
    [prop]: cb(val, prop)
  }), {});

const sumObjects = (first, second = {}) =>
  reduceObject(first, (result, val, prop) =>
    assign(result, {
      [prop]: (val || 0) + (second[prop] || 0)
    }), {}
  );

const sumShiftsPerDate = (data) =>
  mapObject(data, (shifts, date) =>
    reduceObject(shifts, (result, shift) =>
      reduceObject(shift, (result, machineKeys, machineName) =>
        assign(result, {
          [machineName]: sumObjects(machineKeys, result[machineName])
        }),
        result
      ),
      {}
    )
  );

console.log(sumShiftsPerDate(data));

下面是使用下划线 keys, extend, object, defaults, mapObject and reduce, instead of Object.keys, Object.assign, ES6+ default parameters, ES6+ dynamic keys 和自定义函数 mapObjectreduceObject 的相同方法:

const data = {"28-11":{"S1":{"M1":{"K1":10,"K2":12,"K3":15},"M2":{"K1":8,"K2":6,"K3":5}},"S2":{"M1":{"K1":8,"K2":6,"K3":5},"M2":{"K1":10,"K2":12,"K3":15}}}};

// throw in a mixin just for fun :)
// http://underscorejs.org/#mixin
_.mixin({
  sumObjects: (first, second) => 
    _.reduce(first, (result, val, prop) =>
      _.extend(result, _.object(
        [prop], [(val || 0) + (second[prop] || 0)]
      )), {}
    )
});

const sumShiftsPerDate = (data) =>
  _.mapObject(data, (shifts) =>
    _.reduce(shifts, (result, machines) =>
      _.reduce(machines, (result, machineKeys, machineName) =>
        _.extend(result, _.object(
          [machineName], [_.sumObjects(
            machineKeys,
            _.defaults({}, result[machineName]))]
        )),
        result
      ),
      {}
    )
  );

console.log(sumShiftsPerDate(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

注意:普通 JS 解决方案比下划线解决方案有两个优点:

  1. 它使用本地方法,通常更快更稳定
  2. 它不会仅仅为它需要的几个方法加载整个库

这是我的版本:

const data = {"28-11":{"S1":{"M1":{"K1":10,"K2":12,"K3":15},"M2":{"K1":8,"K2":6,"K3":5}},"S2":{"M1":{"K1":8,"K2":6,"K3":5},"M2":{"K1":10,"K2":12,"K3":15}}}};

var out = {};

Object.keys(data).forEach(date => 
    Object.keys(data[date]).forEach(shift => 
        Object.keys(data[date][shift]).forEach(machine => 
            Object.keys(data[date][shift][machine]).forEach(key => {
                if (out[date] === undefined) out[date] = {};
                if (out[date][machine] === undefined) out[date][machine] = {};
                if (out[date][machine][key] === undefined) out[date][machine][key] = 0;
                out[date][machine][key] += data[date][shift][machine][key];

            })
        )
   )
)
console.log(out)
return out;

还有一种可能。

var data = {"28-11":{"S1":{"M1":{"K1":10,"K2":12,"K3":15},"M2":{"K1":8,"K2":6,"K3":5}},"S2":{"M1":{"K1":8,"K2":6,"K3":5},"M2":{"K1":10,"K2":12,"K3":15}}}};
    
    data = _.mapObject(data, function (days) {
      return _.map(days, function (day) {
        return _.reduce(day, function (acc, machine) {
          return _.extend({}, acc, _.mapObject(machine, function (keyVal, keyKey) {
            return (acc[keyKey] || 0) + keyVal;
          }));
        }, {});
      });
    });
    
    console.log(data);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Here's JSBin.

老实说,如果您想熟练使用 maps 和 reduce 等,我建议您阅读 this RxJS 教程网页。你一开始使用 RxJS 就可以退出,但它教函数式编程开始,它教你像老板一样映射过滤器和减少。