使用 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 },
],
},
]
我想要做的是将其转换为产品的平面列表,其中包含其他信息,例如 inventoryId
、inventoryIndex
和 productIndex
。
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>
这涉及更多代码,但我们所做的是创建一些有用的小函数,并通过组合对这些小函数的调用使我们的主函数更具声明性。在您的项目中可能值得也可能不值得,但肯定值得考虑。
我是函数式编程和 ramda 的新手。我有一个案例,可以很容易地以命令式的方式解决,但我很难用声明式的方式解决。 我有以下结构,它描述了多个库存。
inventories = [
{
id: 'Berlin',
products: [{ sku: '123', amount: 99 }],
},
{
id: 'Paris',
products: [
{ sku: '456', amount: 3 },
{ sku: '789', amount: 777 },
],
},
]
我想要做的是将其转换为产品的平面列表,其中包含其他信息,例如 inventoryId
、inventoryIndex
和 productIndex
。
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>
这涉及更多代码,但我们所做的是创建一些有用的小函数,并通过组合对这些小函数的调用使我们的主函数更具声明性。在您的项目中可能值得也可能不值得,但肯定值得考虑。