【问题标题】:Functional javascript rearrange array index according to some mapping功能性javascript根据一些映射重新排列数组索引
【发布时间】:2017-02-18 05:41:04
【问题描述】:

我有一个像这样的索引映射:

var idxMapping = {
    abc: 1,
    foo: 2,
    bar: 0
};

还有一个对象数组:

var data = [{
    id: 'abc',
    info1: 'random info 1',
    info2: 'random info 2',
}, {
    id: 'foo',
    info1: 'random info 1',
    info2: 'random info 2',
}]

使用函数式编程范例,我如何创建一个新数组,以便根据 idxMapping 中指定的索引转换数据。

结果应该类似于:

[
null, 
{ 
    id: 'abc',
    info1: 'random abc info 1',
    info2: 'random abc info 2'
},
{ 
    id: 'foo',
    info1: 'random foo info 1',
    info2: 'random foo info 2'
}
]

第一个元素为空,因为数据数组不包含任何映射到索引 0 的数据(即本例中的 No bar 对象)。

这就是我使用 forEach 循环实现的方式:

var result = [];

for (var key in idxMapping) {
    result.append(null);
}

data.forEach(function(item) {
    result[idxMapping[item.id]] = item;
});

我需要一个严格遵循函数式编程范例的解决方案。

【问题讨论】:

  • 展示你想要做的事情,如果你知道如何用循环来做,也展示出来。
  • 您是否意识到您得到的答案比您现有的代码差很多(在可读性和效率方面)?
  • @georg 我刚开始使用 FP,并希望重构我现有的项目以使其尽可能实用。但是,我仍然有点不确定 FP 何时是过度杀伤力。这个问题是我实际问题的一部分。我需要这部分代码在另一个两级嵌套映射函数中。我是 FP 的初学者,可能会过度使用 FP。你能指点一下我应该选择命令式编程而不是函数式编程吗?
  • @jgr0:JS 内置的“函数式工具”很差,如果你想“品尝”真正的函数式编程,我建议学习ramda。同时,只需使用最简单的方法——在这种情况下,一个简单的循环就可以了。

标签: javascript arrays functional-programming lodash data-manipulation


【解决方案1】:

函数式编程风格有一种可能的方法:

var idxMapping = {
    abc: 1,
    foo: 2,
    bar: 0
};
var data = [{
    id: 'abc',
    info1: 'random info 1',
    info2: 'random info 2',
}, {
    id: 'foo',
    info1: 'random info 1',
    info2: 'random info 2',
}]

Object.keys(idxMapping)
.sort((fstKey, scndKey) => idxMapping[fstKey] - idxMapping[scndKey])
.map(key => data.find(el => el.id === key))
.map(el => !!el ? el : null)

Object.keys(idxMapping)
.sort((fstKey, scndKey) => idxMapping[fstKey] - idxMapping[scndKey])
.map(key => { var obj = data.find(el => el.id === key); return !!obj ? obj : null })

【讨论】:

  • 这很接近,但不准确。你试过了吗?
  • 我认为你实际上只需要在之后根据idxMapping进行排序
【解决方案2】:

可以这样做:

var result = data.reduce ( 
    (acc, obj) => Object.assign(acc, { [idxMapping[obj.id]]: obj }), 
    Object.keys(idxMapping).map( _ => null) );

var idxMapping = {
    abc: 1,
    foo: 2,
    bar: 0
};

var data = [{
    id: 'abc',
    info1: 'random info 1',
    info2: 'random info 2',
}, {
    id: 'foo',
    info1: 'random info 1',
    info2: 'random info 2',
}];

var result = data.reduce ( 
    (acc, obj) => Object.assign(acc, { [idxMapping[obj.id]]: obj }), 
    Object.keys(idxMapping).map( _ => null) );

console.log(result);

这假设idxMapping 中提到的索引在0..n-1 范围内,其中n 是该数组中的元素数。

如果可以在结果数组中使用undefined 而不是null,那么Object.keys() 部分可以只替换为[]

说明

首先创建一个数组,其中包含与idxMapping 中的键一样多的null 条目:

Object.keys(idxMapping).map( _ => null)

这成为reduce 的初始累加器值,然后迭代data 的每个元素。在每次迭代中,从idxMapping 检索相应的索引:

idxMapping[obj.id]

使用computed property names 的 ES6 语法,这个值(一个数字)被用作(累加器)数组对象的键(它实际上是数组的索引,但数组的索引本质上是对象属性) :

[idxMapping[obj.id]]

...并且数据元素在对象文字中分配给它:

{ [idxMapping[obj.id]]: obj }

例如,上面可能会解决这个问题:

{ '1': obj }

...以及进一步:

{ '1': {
    id: 'abc',
    info1: 'random info 1',
    info2: 'random info 2',
} }

这个对象然后与已经存在的累加器对象合并,使用Object.assign

Object.assign(acc, { [idxMapping[obj.id]]: obj })

...归结为将(累积)结果数组的条目之一设置为正确的值。继续上面的例子,你实际上得到了这个赋值:

acc[1] = {
    id: 'abc',
    info1: 'random info 1',
    info2: 'random info 2',
}

这个数组也是Object.assign的返回值,这很好,因为reduce期望内部回调函数返回累加器的新值,并在下一次迭代中将其传递回回调函数。

在最后一次迭代中,这个返回值变成了reduce本身的返回值,因此也变成了result的值。

【讨论】:

  • 您能解释一下{ [idxMapping[obj.id]]: obj } 部分的工作原理吗?
  • 我在回答中添加了一些解释。希望它能为你澄清事情。
  • 感谢您的澄清!与 ES6 计算属性名称语法混淆。
【解决方案3】:

我可能会用一个衬里来做,如下所示;

var idxMapping = {
                  abc: 1,
                  foo: 2,
                  bar: 0
                 },
          data = [{
          	       id: 'abc',
                info1: 'random info 1',
                info2: 'random info 2',
                  },
                  {
                   id: 'foo',
                info1: 'random info 1',
                info2: 'random info 2',
                  }],
        result = data.reduce((p,c) => (p[idxMapping[c.id]]=c,p),Array(Math.max(...Object.values(idxMapping))+1).fill(null));
console.log(result);

Object.values() 应该可以使用 FF v50 和 Chrone v53

reduce Array(Math.max(...Object.values(idxMapping))+1).fill(null) 的初始部分只是创建了一个由 nulls 组成的数组,大小为 idxMapping + 1 中的最大值,其余的只是减少。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-10
    • 2017-04-08
    • 2014-12-30
    相关资源
    最近更新 更多