代码之家  ›  专栏  ›  技术社区  ›  Tomasz Lempart

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

  •  0
  • Tomasz Lempart  · 技术社区  · 3 年前

    我是函数式编程和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解决这个问题时。我不知道如何进入 库存Id ,同时绘制产品图。如果能看到一段使用ramda编写的代码,那就太好了,它的功能与上面的代码相同。

    干杯, 托马什

    0 回复  |  直到 3 年前
        1
  •  1
  •   Aadit M Shah    3 年前

    您可以使用以下工具轻松完成此操作 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);

    请注意 平面地图 被称为 chain 在拉姆达,但你需要 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>
        2
  •  0
  •   Scott Sauyet    3 年前

    我可能会以与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 ,我们通过提升 产品 数组与其父数组合并。

    所有这些都是潜在的可重用转换。如果我们愿意,我们可以为这些函数分解出单独的辅助函数,然后使用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控制台包装器{最大高度:100%!重要;顶部:0}
    <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>

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