Functional programming 映射时如何合并两个数组?

Functional programming 映射时如何合并两个数组?,functional-programming,ramda.js,Functional Programming,Ramda.js,背景:我对Ramda和FP相当陌生。我发现自己遇到了这样的情况:我有两个输入itemList和costList。列表中的值具有基于idx的关系。因此itemList[0]和costList[0]表示可以在同一对象中的值(例如{item:itemList[0],cost:costList[0]})。因此,另一种解决方案可以从合并itemList和costList开始。我对其中一种或两种解决方案都感兴趣。任何其他提示和建议也将不胜感激 let itemList = ['shirt', 'shoe

背景:我对Ramda和FP相当陌生。我发现自己遇到了这样的情况:我有两个输入
itemList
costList
。列表中的值具有基于idx的关系。因此
itemList[0]
costList[0]
表示可以在同一对象中的值(例如
{item:itemList[0],cost:costList[0]}
)。因此,另一种解决方案可以从合并itemList和costList开始。我对其中一种或两种解决方案都感兴趣。任何其他提示和建议也将不胜感激

  let itemList = ['shirt', 'shoes'];
  let costList = ['shirtCost', 'shoesCost'];

  let formulaList = R.map(
    x => ({
      formulaXAttr: x,
      formulaYAttr: 'tbd later',
    })
  )(itemList);

  let finalList = R.map(
    x => R.merge(x, {formulaYAttr: '???'})  // how to merge in costList?
  )(formulaList);

  // this is how i'd do it in vanilla JS
  let finalList = formulaList.map((x, idx) => ({
    ...x,
    formulaYAttr: costList[idx],
  }));

您在Ramda中寻找的函数被调用,它接受一个需要两个参数的函数,第一个列表中的每个元素一个参数,第二个列表中的成对元素一个参数。这将作为一个列表结束,该列表与提供的两个列表中的较短列表一样长,包含为每对调用函数的结果

const itemList=['shirt','shoes'];
const costList=['shirtCost','shoesCost'];
常数fn=R.zipWith((x,y)=>
({formulaXAttr:x,formulaYAttr:y}))
console.log(
fn(项目清单、成本清单)
)

斯科特·克里斯托弗的答案显然是正确的
zipWith
正是为这种情况而设计的。在标题中,您将询问如何在映射时执行此操作。我想指出的是,尽管拉姆达确实为此提供了一个选项(详情请参见),但人们普遍不赞成它。这样做的原因对于更多地了解FP很重要

map
的一种理解是,它通过对每个元素应用给定函数,将一种类型的元素列表转换为另一种类型的元素列表。这是一个很好的公式,但有一个更一般的公式:
map
通过对每个元素应用给定函数,将一种类型的元素容器转换为另一种类型的元素容器。换句话说,
映射的概念可以应用于许多不同的容器,而不仅仅是列表。该规范为具有
map
方法的容器定义了一些规则。具体类型为,但对于那些没有相关数学背景的人,这可以被认为是
可映射的
。Ramda应能与以下类型进行良好的互操作:

const square = n => n * n;
map(square, [1, 2, 3, 4, 5])  //=> [1, 4, 9, 16, 25]


// But also, if MyContainer is a Functor
map(square, MyContainer(7)) //=> MyContainer(49)


// and, for example, if for some Maybe Functor with Just and Nothing subtypes
const {Just, Nothing} = {Maybe}
// then
map(square, Just(6)) //=> Just(36)
map(square, Nothing()) //=> Nothing()
Ramda的
map
函数只使用容器的元素调用提供的转换函数。它不提供索引。这是有意义的,因为并非所有容器都有索引的概念

正因为如此,映射是不正确的,Ramda不是尝试匹配索引的地方。但这是拉姆达的核心。如果您想合并两个元素通过索引匹配的列表,您可以像Scott使用
zipWith
做的那样简单。但您也可以使用
zip
,其工作原理如下:

zip(['a', 'b', 'c', 'd'], [2, 3, 5, 7]) //=> [['a', 2], ['b', 3], ['c', 5], ['d', 7]]

然后对结果对调用
map
zipWith
只需一步即可完成同样的操作。但是如果没有它,像
map
zip
这样的原语仍然可以组合起来做你想做的事情。

除了这里的其他答案,我想向你展示如何自己编写这种东西。Ramda库并不总是为您提供一行解决方案,如果您将解决方案限制为仅使用Ramda提供的函数,那么您将有所保留

这种做法将使您有信心尝试编写自己的程序。当/如果您发现Ramda具有一些您需要的功能时,您可以轻松地重构程序以使用Ramda提供的功能

const无=
符号()
常数zipWith=(f[x=None,…xs],[y=None,…ys])=>
x==无| | y==无
? []
:[f(x,y)].concat(zipWith(f,x,y))
console.log
(齐普威思)
((名称,价格)=>({name,price})
,[“鞋子”,“裤子”]
, [ 19.99, 29.99 ]
)
)
//[{名称:“鞋”,价格:19.99}
//,{名称:“裤子”,价格:29.99}

//]
如果要使用一个附加参数,为什么不使用默认为
[]
的累加器,只需使用列表的长度在基本大小写和递归大小写之间切换?有了这一点,您可以在可用的情况下利用尾部调用优化。
None
符号是一种有趣的方法,但我发现它更难一目了然。感谢您的详细解释。我在转换和聚合JSON负载时遇到了这个问题
zipWith
帮助我解决最初的问题。但是,我对使用
zipWith
的解决方案并不完全满意。希望我能找到解决类似问题的另一种方法。你会说如果我过于依赖
zip
函数,那么这是否意味着有什么(不好的)东西?如果是这样,我处理这个问题的方式与使用
zip
有什么不同?@epikhighs:我想说
zip
对于处理来自其他来源的数据非常有用。但是,如果你在自己的数据结构内部大量使用它,那么最好考虑其他的结构。使用共享索引既尴尬又有点太复杂。