使用 Ramda 将对象数组与父 属性 一起映射

Map array of objects together with the parent property using Ramda

我是函数式编程和 ramda 的新手。我有一个案例,可以很容易地以命令式的方式解决,但我很难用声明式的方式解决。 我有以下结构,它描述了多个库存。

inventories = [
  {
    id: 'Berlin',
    products: [{ sku: '123', amount: 99 }],
  },
  {
    id: 'Paris',
    products: [
      { sku: '456', amount: 3 },
      { sku: '789', amount: 777 },
    ],
  },
]

我想要做的是将其转换为产品的平面列表,其中包含其他信息,例如 inventoryIdinventoryIndexproductIndex

products = [
  { inventoryId: 'Berlin', inventoryIndex: 1, sku: '123', amount: 99, productIndex: 1 },
  { inventoryId: 'Paris', inventoryIndex: 2, sku: '456', amount: 3, productIndex: 1 },
  { inventoryId: 'Paris', inventoryIndex: 2, sku: '789', amount: 777, productIndex: 2 },
]

正如我之前所写,以命令方式执行此操作不是问题。

function enrichProductsWithInventoryId(inventories) {
  const products = []
  for (const [inventoryIndex, inventory] of inventories.entries()) {
    for (const [productIndex, product] of inventory.products.entries()) {
      product.inventoryId = inventory.id
      product.inventoryIndex = inventoryIndex + 1
      product.productIndex = productIndex + 1
      products.push(product)
    }
  }

  return products
}

问题是当我尝试用 ramda 解决它时。我不知道如何在映射产品时访问 inventoryId。能看到一段用ramda写的代码就好了,和上面的一样。

干杯, 托马斯

您可以使用 flatMap 轻松完成此操作。

const inventories = [
    {
        id: "Berlin",
        products: [{ sku: "123", amount: 99 }]
    },
    {
        id: "Paris",
        products: [
            { sku: "456", amount: 3 },
            { sku: "789", amount: 777 }
        ]
    }
];

const products = inventories.flatMap((inventory, inventoryIndex) =>
    inventory.products.map((product, productIndex) => ({
        inventoryId: inventory.id,
        inventoryIndex: inventoryIndex + 1,
        sku: product.sku,
        amount: product.amount,
        productIndex: productIndex + 1
    })));

console.log(products);

注意 flatMap 被称为 chain in Ramda, but you'll need to addIndex

const inventories = [
    {
        id: "Berlin",
        products: [{ sku: "123", amount: 99 }]
    },
    {
        id: "Paris",
        products: [
            { sku: "456", amount: 3 },
            { sku: "789", amount: 777 }
        ]
    }
];

const chainIndexed = R.addIndex(R.chain);
const mapIndexed = R.addIndex(R.map);

const products = chainIndexed((inventory, inventoryIndex) =>
    mapIndexed((product, productIndex) => ({
        inventoryId: inventory.id,
        inventoryIndex: inventoryIndex + 1,
        sku: product.sku,
        amount: product.amount,
        productIndex: productIndex + 1
    }), inventory.products), inventories);

console.log(products);
<script src="https://unpkg.com/ramda@0.27.1/dist/ramda.min.js"></script>

我可能会按照 Aadit 建议的类似方式来执行此操作,尽管我会稍微不同地构建它,并使用参数解构

const convert = (inventories) =>
  inventories .flatMap (({id: inventoryId, products}, i, _, inventoryIndex = i + 1) =>
    products.map (
      ({sku, amount}, i, _, productIndex = i + 1) => 
        ({inventoryId, inventoryIndex, sku, amount, productIndex})
    )
  )

const inventories = [{id: "Berlin", products: [{sku: "123", amount: 99}]}, {id: "Paris", products: [{sku: "456", amount: 3}, {sku: "789", amount: 777}]}]

console .log (convert (inventories));
.as-console-wrapper {max-height: 100% !important; top: 0}

但是,如果您想将其分解成更小的组件,Ramda 可以帮助您编写它们并将它们粘合成一个整体。如果我们试图描述我们正在做的事情,我们可能会将其视为四个步骤。我们将 id 字段重命名为 inventoryId,我们将 运行 索引添加到我们的库存中,并将索引分开到每组 products,并且我们 denormalize/flatten 我们的嵌套通过提升 products 数组与它们的父级合并来列出列表。

所有这些都可能是可重用的转换。如果我们愿意,我们可以为它们分解出单独的辅助函数,然后使用 Ramda 将它们组合成一个函数。它可能看起来像这样:

const {pipe, toPairs, map, fromPairs, addIndex, chain, merge, evolve} = R

const mapKeys = (cfg) => pipe (
  toPairs,
  map (([k, v]) => [cfg [k] || k, v]),
  fromPairs
)
 
const addOrdinals = (name) => 
  addIndex (map) ((x, i) => ({... x, [name]: i + 1 }))

const promote = (name) => 
  chain (({[name]: children, ...rest}) => map (merge(rest), children))

const transform = pipe (
  map (mapKeys ({id: 'inventoryId'})),
  addOrdinals ('inventoryIndex'),
  map (evolve ({products: addOrdinals ('productIndex')})),
  promote ('products')
)

const inventories = [{id: "Berlin", products: [{sku: "123", amount: 99}]}, {id: "Paris", products: [{sku: "456", amount: 3}, {sku: "789", amount: 777}]}]

console .log (transform (inventories))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>

这涉及更多代码,但我们所做的是创建一些有用的小函数,并通过组合对这些小函数的调用使我们的主函数更具声明性。在您的项目中可能值得也可能不值得,但肯定值得考虑。