在名称匹配的深度嵌套数据结构中更改属性

时间:2018-11-25 12:00:16

标签: javascript

我有一些类似的数据结构:

{
  "category": "Fruits",
  "department": "ABC123",
  "content": {
    "subcategory1": [
      {
        "fruitName": "Apples",
        "inStock": false
      },
      {
        "fruitName": "Pears",
        "inStock": false
      }
    ],
    "subcategory2": [
      {
        "fruitName": "Oranges",
        "inStock": false
      },
      {
        "fruitName": "Lemons",
        "inStock": false
      }
    ]
  }
}

我希望能够基于一些输入来更新“ inStock”值。我有一个像这样的数组:

["Pears", "Oranges"]

对于该数组中属性的所有实例,我想将inStock更新为true以结束:

{
  "category": "Fruits",
  "department": "ABC123",
  "content": {
    "subcategory1": [
      {
        "fruitName": "Apples",
        "inStock": false
      },
      {
        "fruitName": "Pears",
        "inStock": true
      }
    ],
    "subcategory2": [
      {
        "fruitName": "Oranges",
        "inStock": true
      },
      {
        "fruitName": "Lemons",
        "inStock": false
      }
    ]
  }
}

我想为此编写一些通用函数,以便可以执行类似const newData = updateStock( oldData );的操作。但是我不确定如何开始写这篇文章。如果我能获得一些入门的指导(即使不是解决方案),我也将非常感激。

2 个答案:

答案 0 :(得分:1)

请始终在OP中发布您的尝试,以便我们了解您的处理方式。

您可以尝试使用Array.filterArray.forEach

let data = {
  "category": "Fruits",
  "department": "ABC123",
  "content": {
    "subcategory1": [
      {
        "fruitName": "Apples",
        "inStock": false
      },
      {
        "fruitName": "Pears",
        "inStock": false
      }
    ],
    "subcategory2": [
      {
        "fruitName": "Oranges",
        "inStock": false
      },
      {
        "fruitName": "Lemons",
        "inStock": false
      }
    ]
  }
}

function updateStock(names) {
  Object.values(data.content).flat()
        .filter(d => names.includes(d.fruitName))
        .forEach(d => d.inStock = true)
}

updateStock(['Oranges', 'Pears'])

console.log(data)

答案 1 :(得分:0)

在我看到@NitishNarang的答案之前,我开始写这篇文章,所以我将对其进行扩展和扩展。

首先将您的问题分解成小块。

首先,您想要一种更新对象上的inStock属性的方法。稍后,我们将继续更新嵌套数据结构中的多个对象。

我将在这里采用一种实用的方法。让我们写一个高阶函数(可以说是“工厂”函数)。当使用属性名称和值调用时,它将返回一个新函数。当使用对象调用此新功能时,该函数将使用设置为指定值的属性来更新该对象:

const updateProperty = (key, value) => obj => {
  obj[key] = value;
  return obj;
};

请注意,此功能可用于所有方式。如果需要,我们可以创建一个函数,以将material的键更新为值chocolate

const renderUseless = updateProperty('material', 'chocolate');

示例:

const teapot = { material: 'china' };
renderUseless(teapot);

无论如何,我们可以使用updateProperty做更多有用的事情,例如创建方便功能以将商品标记为有货或无货:

const setInStock = updateProperty('inStock', true);
const setOutOfStock = updateProperty('inStock', false);

现在,我们如何将一堆东西标记为有货?

const products = [
  {
    fruitName: 'Apples',
    inStock: false
  },
  {
    fruitName: 'Pears',
    inStock: true
  }
];

products.forEach(setInStock);

那么我们怎么可能只为那些与给定谓词匹配的元素运行函数呢?好吧,我们可以使用Array.filter,当给定一个数组时,它返回一个仅包含那些通过给定谓词的项目的新数组。

让我们构建一个接受水果名称数组的函数,并返回一个谓词,如果其输入对象具有包含在该数组中的true属性,则返回一个谓词,该谓词返回fruitName,并且返回{{1} }否则:

false

const hasFruitName = names => item => names.includes(item.fruitName); 结合使用:

Array.filter

因此,我们现在有一个解决方案,用于处理一系列商品。我们只需要处理您的嵌套数据结构。假设您有一个包含多个数组的嵌套对象,那么我们如何创建一个平面的项目数组?

const products = [ { fruitName: 'Apples', inStock: false }, { fruitName: 'Pears', inStock: true } ]; const matchingProducts = products.filter(isFruitName(['Apples'])); 将从对象Object.values(data.content)中获取值,从而产生一个数组数组。要将其展平为单个数组,我们可以使用data.content,但尚未得到广泛支持。让我们做一个等效的可移植函数:

Array.flat

现在让我们创建另一个便捷功能:

const flattenNestedArray = arr => [].concat.apply([], arr);

将所有内容放在一起,我们得到:

const getItemsFromData = data =>
  flattenNestedArray(Object.values(data.content));

这有点过分设计,但是希望它将说明从小的通用通用高阶函数开始的好处。最终,您会得到许多小的,可重复使用的帮助程序,这些帮助程序会成长为稍微更专业的构建块。一切都清楚地命名,到最终实现时,一目了然。

最后,我添加了const setFruitInStock = (fruitNames, data) => getItemsFromData(data) .filter(hasFruitName(fruitNames)) .forEach(setInStock); 作为第二个参数,因此您的函数更具可重用性。

我将保留纯净度(即不改变输入数据)作为OP的练习:)