映射添加/减少具有相同索引的两个数组对象

Kev*_*nHu 5 javascript arrays json functional-programming ecmascript-6

我有两个数组对象如下:

var arr1 = [
    {
        name: 1,
        value: 10
    },
    {
        name: 2,
        value: 15
    }
]

var arr2 = [
    {
        name: 3,
        value: 5
    },
    {
        name: 4,
        value: 3
    }
]
Run Code Online (Sandbox Code Playgroud)

我想重新定义键并使用相同的索引减少每个数据。

输出:

var arr1 = [
    {
        itemLabel: 1,
        itemValue: 5
    }, 
    {
        itemLabel: 2,
        itemValue: 12
    }
]
Run Code Online (Sandbox Code Playgroud)

我现在做如下:

formatData = arr1.map((row, index) => ({
    itemLabel: arr1.name,
    itemValue: arr1.value - arr2[index].value
}))
Run Code Online (Sandbox Code Playgroud)

这样做有没有更好的解决方案?

Tha*_*you 1

孤胆英雄

\n\n

一个简单的递归程序,可以在单个函数中处理所有内容。这里有一个明显的混合问题,这损害了函数的整体可读性。我们将在下面看到解决此问题的一种方法

\n\n

\r\n
\r\n
const main = ([x, ...xs], [y, ...ys]) =>\r\n  x === undefined || y === undefined\r\n    ? []\r\n    : [ { itemLabel: x.name, itemValue: x.value - y.value } ] .concat (main (xs, ys))\r\n\r\nconst arr1 =\r\n  [ { name: 1, value: 10 }, { name: 2, value: 15 } ]\r\n\r\nconst arr2 =\r\n  [ { name: 3, value: 5 }, { name: 4, value: 3 } ]\r\n  \r\nconsole.log (main (arr1, arr2))\r\n// [ { itemLabel: 1, itemValue: 5 },\r\n//   { itemLabel: 2, itemValue: 12 } ]
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n


\n\n

用类型思考

\n\n

这部分答案受到 Monoid 类别 \xe2\x80\x93 类型论的影响,我不会深入探讨它,因为我认为代码应该能够证明自己。

\n\n

所以我们的问题有两种类型:我们称它们为 Foo 和 Bar

\n\n
    \n
  • Foo\xe2\x80\x93 具有name, 和value字段
  • \n
  • Bar\xe2\x80\x93 有itemLabelitemValue字段
  • \n
\n\n

我们可以随心所欲地表示我们的“类型”,但我选择了一个简单的函数来构造一个对象

\n\n
const Foo = (name, value) =>\n  ({ name\n   , value\n   })\n\nconst Bar = (itemLabel, itemValue) =>\n  ({ itemLabel\n   , itemValue\n   })\n
Run Code Online (Sandbox Code Playgroud)\n\n

生成类型值

\n\n

要构造我们类型的新值,我们只需将函数应用于字段值

\n\n
const arr1 =\n  [ Foo (1, 10), Foo (2, 15) ]\n\nconst arr2 =\n  [ Foo (3, 5), Foo (4, 3) ]\n
Run Code Online (Sandbox Code Playgroud)\n\n

让我们看看到目前为止我们拥有的数据

\n\n
console.log (arr1)\n// [ { name: 1, value: 10 },\n//   { name: 2, value: 15 } ]\n\nconsole.log (arr2)\n// [ { name: 3, value: 5 },\n//   { name: 4, value: 3 } ]\n
Run Code Online (Sandbox Code Playgroud)\n\n

一些高层规划

\n\n

我们有了一个良好的开端。我们有两个 Foo 值数组。我们的目标是通过从每个数组中获取一个 Foo 值,将它们组合起来(稍后详细介绍)来处理这两个数组,然后移至下一对

\n\n
const zip = ([ x, ...xs ], [ y, ...ys ]) =>\n  x === undefined || y === undefined\n    ? []\n    : [ [ x, y ] ] .concat (zip (xs, ys))\n\nconsole.log (zip (arr1, arr2))\n// [ [ { name: 1, value: 10 },\n//     { name: 3, value: 5 } ],\n//   [ { name: 2, value: 15 },\n//     { name: 4, value: 3 } ] ]\n
Run Code Online (Sandbox Code Playgroud)\n\n

组合值:concat

\n\n

通过将 Foo 值正确分组在一起,我们现在可以更多地关注组合过程是什么。在这里,我将定义一个泛型concat,然后在我们的 Foo 类型上实现它

\n\n
// generic concat\nconst concat = (m1, m2) =>\n  m1.concat (m2)\n\nconst Foo = (name, value) =>\n  ({ name\n   , value\n   , concat: ({name:_, value:value2}) =>\n       // keep the name from the first, subtract value2 from value\n       Foo (name, value - value2)\n   })\n\nconsole.log (concat (Foo (1, 10), Foo (3, 5)))\n// { name: 1, value: 5, concat: [Function] }\n
Run Code Online (Sandbox Code Playgroud)\n\n

听起来是不是concat很熟悉?数组和字符串也是 Monoid 类型!

\n\n
concat ([ 1, 2 ], [ 3, 4 ])\n// [ 1, 2, 3, 4 ]\n\nconcat (\'foo\', \'bar\')\n// \'foobar\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

高阶函数

\n\n

现在我们有办法将两个 Foo 值组合在一起。name保留第一个 Foo 的 ,并减去value属性。现在我们将其应用于“压缩”结果中的每一对。函数式程序员喜欢高阶函数,所以你会欣赏这种高阶和谐

\n\n
const apply = f => xs =>\n  f (...xs)\n\nzip (arr1, arr2) .map (apply (concat))\n// [ { name: 1, value: 5, concat: [Function] },\n//   { name: 2, value: 12, concat: [Function] } ]\n
Run Code Online (Sandbox Code Playgroud)\n\n

转换类型

\n\n

现在我们有了正确的 Foo 值namevalue值,但我们希望最终答案是 Bar 值。我们只需要一个专门的构造函数

\n\n
Bar.fromFoo = ({ name, value }) =>\n  Bar (name, value)\n\nBar.fromFoo (Foo (1,2))\n// { itemLabel: 1, itemValue: 2 }\n\nzip (arr1, arr2)\n  .map (apply (concat))\n  .map (Bar.fromFoo)\n// [ { itemLabel: 1, itemValue: 5 },\n//   { itemLabel: 2, itemValue: 12 } ]\n
Run Code Online (Sandbox Code Playgroud)\n\n

付出就有收获

\n\n

一种美丽、纯粹的功能性表达。我们的程序读起来非常好;由于声明式的风格,数据的流动和转换很容易遵循。

\n\n
// main :: ([Foo], [Foo]) -> [Bar]\nconst main = (xs, ys) =>\n  zip (xs, ys)\n    .map (apply (concat))\n    .map (Bar.fromFoo)\n
Run Code Online (Sandbox Code Playgroud)\n\n

当然还有完整的代码演示

\n\n

\r\n
\r\n
const Foo = (name, value) =>\n  ({ name\n   , value\n   })\n\nconst Bar = (itemLabel, itemValue) =>\n  ({ itemLabel\n   , itemValue\n   })\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n


\n\n

评论

\n\n

我们上面的程序是用链实现的.map.map这意味着多次处理和创建中间值。[[x1,y1], [x2,y2], ...]我们还在调用中创建了一个中间数组zip。范畴论为我们提供了诸如等式推理之类的东西,因此我们可以替换m.map(f).map(g)m.map(compose(f,g))获得相同的结果。因此,这一点还有改进的空间,但我认为这足以让你咬牙切齿并开始以不同的方式思考问题。

\n