映射添加/减少两个具有相同索引的数组对象 [英] map add/reduce two array object with same index

查看:77
本文介绍了映射添加/减少两个具有相同索引的数组对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个数组对象如下:

  var arr1 = [
{
name: 1,
值:10
},
{
名称:2,
值:15
}
]

var arr2 = [
{
名称:3,
值:5
},
{
名称:4,
值:3
}
]

我想重新定义密钥并减少每个数据具有相同的索引。



输出:

  var arr1 = [
{
itemLabel:1,
itemValue:5
},
{
itemLabel:2,
itemValue:12


$ / code $ / pre

现在我在做如下操作:

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

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

>一个简单的递归程序,处理单个函数中的所有内容。这里有一个明确的关注点,这会影响功能的整体可读性。我们将在下面看到针对此问题的一种解决方法。

const main =( [x,... xs],[y,... ys])=> x === undefined || y ===未定义? []:[{itemLabel:x.name,itemValue:x.value - y.value}] .concat(main(xs,ys))const arr1 = [{name:1,value:10},{name:2 ,value:15}] const arr2 = [{name:3,value:5},{name:4,value:3}] console.log(main(arr1,arr2))// [{itemLabel:1,itemValue :5},// {itemLabel:2,itemValue:12}]

b
$ b

用类型思考



答案的这部分受到类型理论的影响Monoid类 - 我不会太过分,因为我认为代码应该能够展示自己。



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




  • Foo - 具有名称

  • Bar - 含有 itemLabel itemValue



我们可以代表我们的类型,但是我选择了一个简单的函数,一个对象

  const Foo =(name,value)=> 
({name
,value
})

const Bar =(itemLabel,itemValue)=>
({itemLabel
,itemValue
})

创建一个类型的值



为了构造我们类型的新值,我们只是将函数应用到字段值中。

  const arr1 = 
[Foo(1,10),Foo(2,15)]

const arr2 =
[Foo(3,5),Foo(4,3)]

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

  console.log(arr1)
// [{name:1,value:10 },
// {name:2,value:15}]

console.log(arr2)
// [{name:3,value:5},
// {name:4,value:3}]

等级计划



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

  const zip =([x,... xs],[y,... ys])=> 
x === undefined || y === undefined
? []
:[[x,y]] .concat(zip(xs,ys))

console.log(zip(arr1,arr2))
// [ $ {
// {name:3,value:5}],
// [{name:2,value:15},
/ / {name:4,value:3}]]

合并值:<$ c

通过将Foo值合理分组,我们现在可以更多地关注组合过程的内容。在这里,我将要定义一个通用的 concat ,然后在我们的Foo类型上实现它

  //泛型concat 
const concat =(m1,m2)=>
m1.concat(m2)

const Foo =(name,value)=>
({name
,value
,concat:({name:_,value:value2})=>
//保留第一个名字,从中减去value2价值
Foo(name,value - value2)
})

console.log(concat(Foo(1,10),Foo(3,5)))
// {name:1,value:5,concat:[Function]}

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

  concat([1,2],[3,4])
// [1,2,3,4]

concat('foo','bar')
//'foobar'

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

  const apply = f => xs => 
f(... xs)

zip(arr1,arr2).map(apply(concat))
// [{name:1,value:5,concat: [函数]},
// {名称:2,值:12,concat:[函数]}]

转换类型



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

  Bar.fromFoo =({name,value})=> 
Bar(name,value)

Bar.fromFoo(Foo(1,2))
// {itemLabel:1,itemValue:2}

$ b .map(bar.fromFoo)
// [{itemLabel:1,itemValue:5},
// {itemLabel:2,itemValue:12}]



一个美丽的,纯粹的功能表达。我们的计划读得非常好,流程和数据转换很容易遵循,这要归功于声明式风格。

  // main ::([Foo], [Foo]) - > [Bar] 
const main =(xs,ys)=>
zip(xs,ys)
.map(apply(concat))
.map(Bar.fromFoo)

一个完整的代码演示,当然是

  const Foo =(name,value)=> ({name,value,concat:({name:_,value:value2})=> Foo(name,value  -  value2)})const Bar =(itemLabel,itemValue)=> ({itemLabel,itemValue})Bar.fromFoo =({name,value})=>条(名称,值)const concat =(m1,m2)=> m1.concat(m2)const apply = f => xs => f(... xs)const zip =([x,... xs],[y,... ys])=> x === undefined || y ===未定义? []:[[x,y]] .concat(zip(xs,ys))const main =(xs,ys)=> zip(xs,ys).map(apply(concat)).map(Bar.fromFoo)const arr1 = [Foo(1,10),Foo(2,15)] const arr2 = [Foo(3,5), Foo(4,3)] console.log(main(arr1,arr2))// [{itemLabel:1,itemValue:5},// {itemLabel:2,itemValue:12}]  

/ p>

我们上面的程序是用 .map - .map zip 时创建了 [[x1,y1],[x2,y2],...] code>。分类理论给了我们诸如等式推理之类的东西,所以我们可以用 m.map(compose(f))来替换 m.map(f).map(g) ,g))并获得相同的结果。所以还有空间来改善这一点,但我认为这足以削减你的牙齿,并开始以不同的方式考虑事情。


I have two array object as following:

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

var arr2 = [
    {
        name: 3,
        value: 5
    },
    {
        name: 4,
        value: 3
    }
]

I want to redefine the key and reduce each data with the same index.

output:

var arr1 = [
    {
        itemLabel: 1,
        itemValue: 5
    }, 
    {
        itemLabel: 2,
        itemValue: 12
    }
]

I'm doing now as following:

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

Is there any better solution of doing this?

解决方案

One-man army

A simple recursive program that handles everything in a single function. There's a clear mixture of concerns here which hurts of function's overall readability. We'll see one such remedy for this problem below

const main = ([x, ...xs], [y, ...ys]) =>
  x === undefined || y === undefined
    ? []
    : [ { itemLabel: x.name, itemValue: x.value - y.value } ] .concat (main (xs, ys))

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

const arr2 =
  [ { name: 3, value: 5 }, { name: 4, value: 3 } ]
  
console.log (main (arr1, arr2))
// [ { itemLabel: 1, itemValue: 5 },
//   { itemLabel: 2, itemValue: 12 } ]


Thinking with types

This part of the answer is influenced by type theory from the Monoid category – I won't go too far into it because I think the code should be able to demonstrate itself.

So we have two types in our problem: We'll call them Foo and Bar

  • Foo – has name, and value fields
  • Bar – has itemLabel and itemValue fields

We can represent our "types" however we want, but I chose a simple function which constructs an object

const Foo = (name, value) =>
  ({ name
   , value
   })

const Bar = (itemLabel, itemValue) =>
  ({ itemLabel
   , itemValue
   })

Making values of a type

To construct new values of our type, we just apply our functions to the field values

const arr1 =
  [ Foo (1, 10), Foo (2, 15) ]

const arr2 =
  [ Foo (3, 5), Foo (4, 3) ]

Let's see the data we have so far

console.log (arr1)
// [ { name: 1, value: 10 },
//   { name: 2, value: 15 } ]

console.log (arr2)
// [ { name: 3, value: 5 },
//   { name: 4, value: 3 } ]

Some high-level planning

We're off to a great start. We have two arrays of Foo values. Our objective is to work through the two arrays by taking one Foo value from each array, combining them (more on this later), and then moving onto the next pair

const zip = ([ x, ...xs ], [ y, ...ys ]) =>
  x === undefined || y === undefined
    ? []
    : [ [ x, y ] ] .concat (zip (xs, ys))

console.log (zip (arr1, arr2))
// [ [ { name: 1, value: 10 },
//     { name: 3, value: 5 } ],
//   [ { name: 2, value: 15 },
//     { name: 4, value: 3 } ] ]

Combining values: concat

With the Foo values properly grouped together, we can now focus more on what that combining process is. Here, I'm going to define a generic concat and then implement it on our Foo type

// generic concat
const concat = (m1, m2) =>
  m1.concat (m2)

const Foo = (name, value) =>
  ({ name
   , value
   , concat: ({name:_, value:value2}) =>
       // keep the name from the first, subtract value2 from value
       Foo (name, value - value2)
   })

console.log (concat (Foo (1, 10), Foo (3, 5)))
// { name: 1, value: 5, concat: [Function] }

Does concat sound familiar? Array and String are also Monoid types!

concat ([ 1, 2 ], [ 3, 4 ])
// [ 1, 2, 3, 4 ]

concat ('foo', 'bar')
// 'foobar'

Higher-order functions

So now we have a way to combine two Foo values together. The name of the first Foo is kept, and the value properties are subtracted. Now we apply this to each pair in our "zipped" result. Functional programmers love higher-order functions, so you'll appreciate this higher-order harmony

const apply = f => xs =>
  f (...xs)

zip (arr1, arr2) .map (apply (concat))
// [ { name: 1, value: 5, concat: [Function] },
//   { name: 2, value: 12, concat: [Function] } ]

Transforming types

So now we have the Foo values with the correct name and value values, but we want our final answer to be Bar values. A specialized constructor is all we need

Bar.fromFoo = ({ name, value }) =>
  Bar (name, value)

Bar.fromFoo (Foo (1,2))
// { itemLabel: 1, itemValue: 2 }

zip (arr1, arr2)
  .map (apply (concat))
  .map (Bar.fromFoo)
// [ { itemLabel: 1, itemValue: 5 },
//   { itemLabel: 2, itemValue: 12 } ]

Hard work pays off

A beautiful, pure functional expression. Our program reads very nicely; flow and transformation of the data is easy to follow thanks to the declarative style.

// main :: ([Foo], [Foo]) -> [Bar]
const main = (xs, ys) =>
  zip (xs, ys)
    .map (apply (concat))
    .map (Bar.fromFoo)

And a complete code demo, of course

const Foo = (name, value) =>
  ({ name
   , value
   , concat: ({name:_, value:value2}) =>
       Foo (name, value - value2)
   })
  
const Bar = (itemLabel, itemValue) =>
  ({ itemLabel
   , itemValue
   })

Bar.fromFoo = ({ name, value }) =>
  Bar (name, value)

const concat = (m1, m2) =>
  m1.concat (m2)
  
const apply = f => xs =>
  f (...xs)  

const zip = ([ x, ...xs ], [ y, ...ys ]) =>
  x === undefined || y === undefined
    ? []
    : [ [ x, y ] ] .concat (zip (xs, ys))

const main = (xs, ys) =>
  zip (xs, ys)
    .map (apply (concat))
    .map (Bar.fromFoo)

const arr1 =
  [ Foo (1, 10), Foo (2, 15) ]
  
const arr2 =
  [ Foo (3, 5), Foo (4, 3) ]

console.log (main (arr1, arr2))
// [ { itemLabel: 1, itemValue: 5 },
//   { itemLabel: 2, itemValue: 12 } ]


Remarks

Our program above is implemented with a .map-.map chain which means handling and creating intermediate values multiple times. We also created an intermediate array of [[x1,y1], [x2,y2], ...] in our call to zip. Category theory gives us things like equational reasoning so we could replace m.map(f).map(g) with m.map(compose(f,g)) and achieve the same result. So there's room to improve this yet, but I think this is just enough to cut your teeth and start thinking about things in a different way.

这篇关于映射添加/减少两个具有相同索引的数组对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆