映射对象数组而不影响所有对象字段

时间:2017-07-16 14:10:37

标签: javascript node.js functional-programming ecmascript-6

我有一个对象数组,我需要以某种方式解析。对象具有一组公共字段和一些不在所有对象中的可选字段。为了示例,a,b,c是公共字段,d,e,f是可选字段。我想对某些字段执行某些操作,例如将ad的值加倍,同时保留其余字段。如果一个对象缺少一个或多个可选字段,它应该保留在结果中。

问题在于我想以纯粹的功能方式完成它,而不是声明一个空数组并推入它。

示例输入:

const input = [
      {
        a: 3,
        b: 'test',
        c: 34,
        d: 'example'
      },
      {
        a: 6,
        b: 'another',
        c: 0,
        e: true,
        f: () => {}
      }
    ];

预期结果:

[
  {
    a: 6,
    b: 'test',
    c: 34,
    d: 'EXAMPLE'
  },
  {
    a: 12,
    b: 'another',
    c: 0,
    e: true,
    f: () => {}
  }
]

到目前为止,我尝试使用map,如此:

const result = input.map(x => ({
  a: 2 * x.a,
  d: x.d.toUppercase()
});

或者像这样:

const result = input.map(x => ({
  a: 2 * x.a,
  b,
  c,
  d: x.d.toUppercase(),
  e,
  f
});

但是这导致对象只包含被操纵的字段,或者包含所有字段,无论它们是否存在于原始对象中。

有关如何实现这一目标的任何建议吗?

6 个答案:

答案 0 :(得分:2)

如果希望对象是不可变的,则需要在进行所需更改之前复制对象。 Object.assign允许您在一个表达式中执行此操作:

const result = input.map(x => Object.assign({}, x,
  { a: 2 * x.a },
  ('d' in x) && { d: x.d.toUpperCase() }
));

const input = [{
    a: 3,
    b: 'test',
    c: 34,
    d: 'example'
  }, {
    a: 6,
    b: 'another',
    c: 0,
    e: true,
    f: () => {}
  }
];

const result = input.map(x => Object.assign({},
  x,
  { a: 2 * x.a },
  ('d' in x) && { d: x.d.toUpperCase() }
));

console.log(result);
.as-console-wrapper { min-height: 100%; }

答案 1 :(得分:1)

此解决方案需要Babel's Object rest spread transform插件,因为Object Rest/Spread Properties for ECMAScript仍处于提案阶段。

创建一个新对象。将原始对象传播到新对象中。如果要更改的属性存在于原始对象中,请将具有新属性值的对象返回到扩展。如果没有,请返回null(点差忽略undefinednull)。新值将覆盖旧值。

const input = [{
    a: 3,
    b: 'test',
    c: 34,
    d: 'example'
  },
  {
    a: 6,
    b: 'another',
    c: 0,
    e: true,
    f: () => {}
  }
];

const result = input.map(x => ({
  ...x, // spread all props of x
  ...('a' in x ? { a: 2 * x.a } : null), // override a if exists
  ...('d' in x ? { d: x.d.toUpperCase() } : null) // override d if exists
}));

console.log(result);

答案 2 :(得分:0)

如果要更改其值的密钥存在,则可以使用map方法并检查每个对象。如果是这样,那么yuo可以相应地改变密钥的值。

const input = [
      {
        a: 3,
        b: 'test',
        c: 34,
        d: 'example'
      },
      {
        a: 6,
        b: 'another',
        c: 0,
        e: true,
        f: () => {}
      }
    ];

const result = input.map(function(obj){
    if(obj.a){
       obj.a = 2 * obj.a;
    }
    if(obj.d){
       obj.d = obj.d.toUpperCase()
    }
    return obj;
});

console.log(result);

答案 3 :(得分:0)

仍然使用

map函数。在这种情况下,您可以检查对象中是否存在键a,然后仅对其值执行必要的操作。

希望此代码段有用

const input = [{
    a: 3,
    b: 'test',
    c: 34,
    d: 'example'
  },
  {
    a: 6,
    b: 'another',
    c: 0,
    e: true,
    f: () => {}
  }
];
input.map(function(elem, index) {
  if (elem.hasOwnProperty('a')) {
    elem.a = elem['a'] * 2;

  }

})
console.log(input)

答案 4 :(得分:0)

在map函数中,您可以先添加条件来操作输入,然后需要返回修改后的对象。

const input = [
      {
        a: 3,
        b: 'test',
        c: 34,
        d: 'example'
      },
      {
        a: 6,
        b: 'another',
        c: 0,
        e: true,
        f: () => {}
      }
    ];
    
let output = input.map(o => {
  if(o.a) { o.a = o.a * 2; }
  if(o.d) { o.d = o.d.toUpperCase(); }
  return o;
});

console.log(output)

输出:

[
  {
    "a": 6,
    "b": "test",
    "c": 34,
    "d": "EXAMPLE"
  },
  {
    "a": 12,
    "b": "another",
    "c": 0,
    "e": true,
    "f": () => {}
  }
]

答案 5 :(得分:0)

可能值得用Google搜索镜头。许多"功能性" javascript库实现它们以轻松链接您描述的各种数据修改。

在幕后,他们可能会使用您已经找到的相同Object.assign方法,但它们允许可读,可重复使用且易于链接的代码。

快速&脏实现以显示语法的变化(es6):



const propLens = prop => ({
  get: obj => obj[prop],
  // Return a new object with the property set to
  // a value
  set: (val, obj) => Object.assign({}, obj, {
    [prop]: val
  }),
  // Return a new object with the property mapped
  // by a function
  over: (fn, obj) => Object.assign({}, obj, {
    [prop]: fn(obj[prop])  
  })
});

// Helpers to create specific set/over functions
const set = (lens, val) => obj => lens.set(val, obj);
const over = (lens, fn) => obj => lens.over(fn, obj);

// Some utils for the example
const { pipe, double, toUpper } = utils();
  
// Now, you can define your modification by a set
// of lens + set/over combinations
const convertData = pipe(
  set(  propLens("d"),  "added" ),
  over( propLens("a"),  double  ),
  over( propLens("b"),  toUpper )
);

// Example data
const item = {a: 5, b: "hello", c: "untouched"};

// Converted copy
const convertedItem = convertData(item);

console.log("Converted:", convertedItem);
console.log("item !== convertedItem ->", item !== convertedItem);

// Utils
function utils() {
  return {
    pipe: (...fns) => x =>
      fns.reduce((acc, f) => f(acc), x),
    double: x => x * 2,
    toUpper: x => x.toUpperCase()
  };
}
  




(请注意,此示例的运行速度比在一个函数中执行所有操作要慢一些,但您可能只会注意到大量数据)

如果您喜欢镜片方法,我建议使用维护良好且经过测试的库(如Ramda)。您甚至可以获得更多令人敬畏的功能,如索引镜头和镜头路径,并且默认情况下都会进行调整。