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)
这样做有没有更好的解决方案?
孤胆英雄
\n\n一个简单的递归程序,可以在单个函数中处理所有内容。这里有一个明显的混合问题,这损害了函数的整体可读性。我们将在下面看到解决此问题的一种方法
\n\nconst 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用类型思考
\n\n这部分答案受到 Monoid 类别 \xe2\x80\x93 类型论的影响,我不会深入探讨它,因为我认为代码应该能够证明自己。
\n\n所以我们的问题有两种类型:我们称它们为 Foo 和 Bar
\n\nFoo
\xe2\x80\x93 具有name
, 和value
字段Bar
\xe2\x80\x93 有itemLabel
和itemValue
字段我们可以随心所欲地表示我们的“类型”,但我选择了一个简单的函数来构造一个对象
\n\nconst 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\nconst 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\nconsole.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\nconst 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
通过将 Foo 值正确分组在一起,我们现在可以更多地关注组合过程是什么。在这里,我将定义一个泛型concat
,然后在我们的 Foo 类型上实现它
// 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 类型!
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
属性。现在我们将其应用于“压缩”结果中的每一对。函数式程序员喜欢高阶函数,所以你会欣赏这种高阶和谐
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 值name
和value
值,但我们希望最终答案是 Bar 值。我们只需要一个专门的构造函数
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\nconst 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评论
\n\n我们上面的程序是用链实现的.map
,.map
这意味着多次处理和创建中间值。[[x1,y1], [x2,y2], ...]
我们还在调用中创建了一个中间数组zip
。范畴论为我们提供了诸如等式推理之类的东西,因此我们可以替换m.map(f).map(g)
并m.map(compose(f,g))
获得相同的结果。因此,这一点还有改进的空间,但我认为这足以让你咬牙切齿并开始以不同的方式思考问题。