【问题标题】:Search for keys in nested object and delete them在嵌套对象中搜索键并删除它们
【发布时间】:2021-10-11 03:27:30
【问题描述】:

我需要从对象中删除某些键,前提是这些键包含在我的“deleteKeys”数组中。

我怎样才能以优化的方式实现这一目标?

代码如下:

const data = {
  "details": [{
    "userId": "user01",
    "documents": [{
      "document": {
        "id": "doc_pp_01",
        "type": "pp",
        "number": "222333444",
        "personName": {
          "first": "JAMES",
          "middle": "JOHNIE",
          "last": "SMITH"
        },
        "nationality": "AL",
        "dateOfBirth": "1990-01-01",
        "issuingCountry": "AL",
        "expiryDate": "2025-01-01",
        "gender": "MALE"
      }
    }]
  }],
  "criteria": {
    "id:": "AB1234",
    "fullName": "James Johnie Smith"
  }
};
const deleteKeys = ["details", "fullName"];

function cleanData(data) {
  for (let elem in data) {
    if (deleteKeys.includes(elem)) {
      delete data[elem];
    }
  }
  return data;
}

console.log(cleanData(data));

预期输出:

{
  "criteria": {
    "id:": "AB1234"
  }
};

我想知道是否可以通过对对象进行字符串化来实现这一点,因为这将是一个更清洁的解决方案。

function assignKey(data) {
  const formattedData = JSON.stringify(data);
  deleteKeys.forEach(function(elem) {
    if (formattedData.includes(elem)) {
      formattedData.replace(elem, '');
    }
  });
  return JSON.parse(formattedData);
}

【问题讨论】:

  • 相反,将它串起来并不干净。您的代码的唯一问题是它只考虑顶层。尝试在 for 循环中调用 cleanData(data[elem]); 以使其递归。

标签: javascript json string object


【解决方案1】:

使用delete 的问题在于您实际上是在更改原始对象。如果您在不同位置使用相同的对象引用,这可能会导致问题。

JSON 方法将不起作用,因为在删除部分字符串后生成的 JSON 字符串无效。您可以完成这项工作,但更容易出错。

我总是喜欢让这种函数返回一个新的 Object 实例。

function removeKeys(obj, keys) {
  if (Array.isArray(obj)) return obj.map(item => removeKeys(item, keys));

  if (typeof obj === 'object' && obj !== null) {
    return Object.keys(obj).reduce((previousValue, key) => {
      return keys.includes(key) ? previousValue : { ...previousValue, [key]: removeKeys(obj[key], keys) };
    }, {});
  }

  return obj;
}

也可以先浅拷贝 Object 并在新 Object 上使用 delete 而不是使用 reduce

function removeKeys(obj, keys) {
  if (Array.isArray(obj)) return obj.map((item) => removeKeys(item, keys));

  if (typeof obj === "object" && obj !== null) {
    return Object.keys(obj).reduce((previousValue, key) => {
      return keys.includes(key)
        ? previousValue
        : { ...previousValue, [key]: removeKeys(obj[key], keys) };
    }, {});
  }

  return obj;
}

const data = {
  details: [
    {
      userId: "user01",
      documents: [
        {
          document: {
            id: "doc_pp_01",
            type: "pp",
            number: "222333444",
            personName: {
              first: "JAMES",
              middle: "JOHNIE",
              last: "SMITH"
            },
            nationality: "AL",
            dateOfBirth: "1990-01-01",
            issuingCountry: "AL",
            expiryDate: "2025-01-01",
            gender: "MALE"
          }
        }
      ]
    }
  ],
  criteria: {
    "id:": "AB1234",
    fullName: "James Johnie Smith"
  }
};

console.log(
  "Without `fullName` and `details`",
  removeKeys(data, ["fullName", "details"])
);
console.log("Without `id:` and `gender`", removeKeys(data, ["id:", "gender"]));

【讨论】:

    【解决方案2】:

    您只需进行一些更改即可使您的函数递归。我将deleteKeys 更改为一个参数,我觉得它更简洁,但不是必需的。

    function cleanData(data, deleteKeys) {
      // There is nothing to be done if `data` is not an object,
      // but for example "user01" or "MALE".
      if (typeof data != "object") return;
      if (!data) return; // null object
      
      for (const key in data) {
        if (deleteKeys.includes(key)) {
          delete data[key];
        } else {
          // If the key is not deleted from the current `data` object,
          // the value should be check for black-listed keys.
          cleanData(data[key], deleteKeys);
        }
      }
    }
    
    const data = {
      "details": [{
        "userId": "user01",
        "documents": [{
          "document": {
            "id": "doc_pp_01",
            "type": "pp",
            "number": "222333444",
            "personName": {
              "first": "JAMES",
              "middle": "JOHNIE",
              "last": "SMITH"
            },
            "nationality": "AL",
            "dateOfBirth": "1990-01-01",
            "issuingCountry": "AL",
            "expiryDate": "2025-01-01",
            "gender": "MALE"
          }
        }]
      }],
      "criteria": {
        "id:": "AB1234",
        "fullName": "James Johnie Smith"
      }
    };
    
    cleanData(data, ["details", "fullName"]);
    console.log(data);

    要记住的一件事是delete 会改变现有对象。这意味着返回data 不是必需的。

    reverse()sort() 这样的函数确实会返回数组,即使它们会改变现有数组。这通常会导致新手 JavaScript 程序员出现错误/问题。

    const foo = ["a", "b", "c"];
    const bar = foo.reverse();
    

    上面的代码表明foobar 是两个不同的数组。不是这种情况。它们都引用同一个数组,因此 foobar 现在都颠倒了。出于这个原因,我经常不在 Stack Overflow 上使用变异代码的返回值。以下示例更好地显示了代码正在发生变化。

    const foo = ["a", "b", "c"];
    foo.reverse();
    

    这就是我个人cleanData()返回的原因。这会迫使用户注意到您的函数正在发生变化。

    对于返回输入还有一些话要说,因为您可以将返回值与其他方法/函数链接起来。但我个人的看法是,误解的弊端超过了链接的好处。尤其是在 Stack Overflow 这样的“学习”平台上。

    【讨论】:

      【解决方案3】:

      如果你想在删除键后删除空数组或对象,下面的实现就可以了。

      function cleanData(data, deletingKeys) {
        function isEmpty(obj) {
          if (obj === null) return true;
          if (Array.isArray(obj))
            return obj.length === 0;
          if (typeof obj === "object")
            return Object.keys(obj).length === 0;
        }
      
        function removeKeyFrom(aData) {
          if (Array.isArray(aData)) {
            const done = aData.reduce((accum, ele) => {
              const done = removeKeyFrom(ele);
              if (!isEmpty(done))
                accum.push(done);
              return accum;
            }, []);
            return done.length > 0 ? done : null;
          }
          if (typeof aData === "object" && aData !== null) {
            const done = Object.keys(aData).reduce((accum, key) => {
              if (!deletingKeys.includes(key)) {
                const done = removeKeyFrom(aData[key]);
                if (!isEmpty(done)) // required for empty object element
                  accum[key] = done;
              }
              return accum;
            }, {});
            return (Object.keys(done).length > 0) ? done : null;
          }
          return aData;
        }
        return removeKeyFrom(data);
      }
      

      假设您的数据和删除条件如下:

      const data = {
        "details2": [{
          "userId": "user01",
          // `documents` should be removed since the value is the array of an empty object
          "documents": [{
            "details": { // this should be removed
              "id": "doc_pp_01",
              "type": "pp",
              "number": "222333444",
              "personName": {
                "first": "JAMES",
                "middle": "JOHNIE",
                "last": "SMITH"
              },
              "nationality": "AL",
              "dateOfBirth": "1990-01-01",
              "issuingCountry": "AL",
              "expiryDate": "2025-01-01",
              "gender": "MALE"
            }
          }]
        }],
        "criteria": {
          "id:": "AB1234",
          "fullName": "James Johnie Smith",
          "shouldBeRemoved": { // this should be removed since the value will be the empty object
            "details": {
              "whatever": 1234
            }
          }
        }
      };
      
      const deleteKeys = ["details", "fullName"];
      

      调用cleanData(data, deleteKeys)会导致

      {
        "details2": [
          {
            "userId": "user01"
          }
        ],
        "criteria": {
          "id:": "AB1234"
        }
      }
      

      而不是

      {
        "details2": [
          {
            "userId": "user01",
            "documents": [
              {}
            ]
          }
        ],
        "criteria": {
          "id:": "AB1234",
          "shouldBeRemoved": {}
        }
      }
      

      这是运行的完整代码。

      function cleanData(data, deletingKeys) {
        function isEmpty(obj) {
          if (obj === null) return true;
          if (Array.isArray(obj))
            return obj.length === 0;
          if (typeof obj === "object")
            return Object.keys(obj).length === 0;
        }
      
        function removeKeyFrom(aData) {
          if (Array.isArray(aData)) {
            const done = aData.reduce((accum, ele) => {
              const done = removeKeyFrom(ele);
              if (!isEmpty(done))
                accum.push(done);
              return accum;
            }, []);
            return done.length > 0 ? done : null;
          }
          if (typeof aData === "object" && aData !== null) {
            const done = Object.keys(aData).reduce((accum, key) => {
              if (!deletingKeys.includes(key)) {
                const done = removeKeyFrom(aData[key]);
                if (!isEmpty(done)) // required for empty object element
                  accum[key] = done;
              }
              return accum;
            }, {});
            return (Object.keys(done).length > 0) ? done : null;
          }
          return aData;
        }
        return removeKeyFrom(data);
      }
      
      const data = {
        "details2": [{
          "userId": "user01",
          // `documents` should be removed since the value is the array of empty object
          "documents": [{
            "details": { // this should be removed
              "id": "doc_pp_01",
              "type": "pp",
              "number": "222333444",
              "personName": {
                "first": "JAMES",
                "middle": "JOHNIE",
                "last": "SMITH"
              },
              "nationality": "AL",
              "dateOfBirth": "1990-01-01",
              "issuingCountry": "AL",
              "expiryDate": "2025-01-01",
              "gender": "MALE"
            }
          }]
        }],
        "criteria": {
          "id:": "AB1234",
          "fullName": "James Johnie Smith",
          "shouldBeRemoved": { // this should be removed since the value is the empty object.
            "details": {
              "whatever": 1234
            }
          }
        }
      };
      
      const deleteKeys = ["details", "fullName"];
      
      const print = obj => JSON.stringify(obj, null, 2);
      
      console.log(print(cleanData(data, deleteKeys)));

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-09-10
        • 1970-01-01
        • 1970-01-01
        • 2015-06-25
        • 1970-01-01
        • 2019-08-13
        • 1970-01-01
        相关资源
        最近更新 更多