首页 > 解决方案 > 迭代具有未知级别的深层嵌套对象并根据用户提供的条件添加删除键/值

问题描述

谁能指导我如何实现我面临的以下挑战?

我有数千个模拟 API 请求响应 JSON 文件。它们嵌套得很深,而且结构都不同。我需要在用户提供的条件匹配的特定位置添加/更新/删除条目。我不确定如何解决这个问题?我试过做类似下面的事情。我正在向用户询问从哪里开始寻找的路径。但这会增加时间,因为用户必须在所有文件中查找路径并将该信息传递给 api。下面的代码只能工作到 2 级。需要搜索所有用户提供条件匹配的完整树,在那个地方,我需要添加/更新/删除数据。我将条件视为一组对象。

守则草案

const _ = require("lodash");
const file = "./sample.json";
const actions = ["add", "delete", "update"];

const consumer = (file, key, where, data, action) => {
    try {
        const act = action.toLowerCase();
        if(!actions.includes(act) throw new Error("invalid action provided");
        if(_.isArray(where) && _.every(where, _.isObject())) throw new Error("no where clause condition provided");
        let content = require(file);
        let typeKeyContent = null;
        let keyContent = _.get(content, key);
        if(!keyContent) throw new Error("invalid key");
        
        if(_.isArray(keyContent)) {
            typeKeyContent = "array"
        } else if (_.isObject(keyContent)) {
            typeKeyContent = "object"
        }
        
        switch (act) {
            case "add":
                if (typeKeyContent === "array") {
                   // array logic
                   for (let i = 0; i < keyContent.length; i++) {
                       const result = where.every(element => {
                            for (let key in element) {
                                return keyContent[key] && element[key] === keyContent[key];
                            }
                       });
                        if (!result) {
                            console.log("attributes matching -> ", result);
                            return;
                        }
                        keyContent[i] = {...keyContent[i], ...data }
                   }
                   let newcontent = _.set(content, key, keyContent);
                    console.log("newcontent -> \n",JSON.stringify(newcontent, null, 2));
                   return;
                }
                const result = where.every(element => {
                    for (let key in element) {
                        return keyContent[key] && element[key] === keyContent[key];
                    }
                });
                if (!result) {
                    console.log("attributes matching -> ", result);
                    return;
                }
                
                keyContent = { ...keyContent, ...data };
                
                let newcontent = _.set(content, key, keyContent);
                console.log("newcontent -> \n",JSON.stringify(newcontent, null, 2));
                // TODO :: store back in json file
                break;
            default:
                console.log("reached default case");
                return;
        }
    } catch(err) {
        console.log("ERROR :: CONSUMER ::", error);
    }
}
// AND based condition only
const conditions = [
    { name: "Essential Large" },
    { selected: true }
];
const newdata = { description: "our best service" } // wants to add new prop
consumer(file, "selected_items.essential", conditions, newdata, "add");

示例 json

{
  "status": 200,
  "request": {},
  "response": {
    "ffs": false,
    "customer": {
      "customer_id": 1544248,
      "z_cx_id": 123456
    },
    "selected_items": {
      "essential": [
        {
          "id": 4122652,
          "name": "Essential Large",
          "selected": true,
          "description": "our best service" // will be added 

        },
        {
          "id": 4122653,
          "name": "Essential Large",
          "selected": true,
          "description": "our best service" // will be added 
        }
      ]
    },
    "service_partner": {
      "id": 3486,
      "name": "Some String",
      "street": "1234 King St."
    },
    "subject": "Project",
    "description": "Issue: (copy/paste service request details here Required"
  }
}

标签: javascriptnode.js

解决方案


所以你想遍历嵌套对象的每个键,对吧?

function forEvery(object,fn){
  //obj is the object, fn is the function
  //this function should go through each item in an object loaded from JSON string
  //fn takes in 3 arguments: current element, that element's parent, level of depth(starts at 1)
    
  var arr=[]
  function recurse(obj,map,depth){
    Object.keys(obj).forEach((a,i)=>{
      fn(obj[a],obj,a,depth) //because fn can affect the object so the if statement should after not before ;-;
      if(typeof obj[a]=="object"&&obj[a]!=null){ //if nested value is another object
        map.push(a); arr.push(map)
        recurse(obj[a],[...map],depth+1)
      }
    })
  }
  recurse(object,[],1)
}

//usage would be like:
//let customerCondition=/*some logic here*/
//let testObj=JSON.parse( (require('fs')).readFileSync('dirToSomeFile.json') )
forEvery(testObj,customerCondition)

这是一个活生生的例子

let testObj={"status":200,"request":{},"response":{"ffs":false,"customer":{"customer_id":1544248,"z_cx_id":123456},"selected_items":{"essential":[{"id":4122652,"name":"Essential Large","selected":true},{"id":4122653,"name":"Essential Medium","selected":false}]},"service_partner":{"id":3486,"name":"Some String","street":"1234 King St."},"subject":"Project","description":"Issue: (copy/paste service request details here Required"}}
function forEvery(object,fn){
  //obj is the object, fn is the function
  //this function should go through each item in an object loaded from JSON string
  //fn takes in 3 arguments: current element, that element's parent, level of depth(starts at 1)
    
  var arr=[]
  function recurse(obj,map,depth){
    Object.keys(obj).forEach((a,i)=>{
      fn(obj[a],obj,a,depth) //because fn can affect the object so the if statement should after not before ;-;
      if(typeof obj[a]=="object"&&obj[a]!=null){ //if nested value is another object
        map.push(a); arr.push(map)
        recurse(obj[a],[...map],depth+1)
      }
    })
  }
  recurse(object,[],1)
}

//example usage
let userQuery=[{ name: "Essential Large" },{ selected: true }]; //the user query in the format you gave
let userCondition={} //assuming each key across userQuery is unique, I set a model object for comparisons later on
userQuery.forEach(obj=>{ //I fill the model object :D
  Object.keys(obj).forEach(key=>{
    userCondition[key]=obj[key]
  })
})
let testFn=(elem,parent,key,depth)=>{
  //I use comparisons with the model object
  let condition=typeof elem!="object"?false:
  Object.keys(userCondition)
  .every(item=>userCondition[item]==elem[item])
  //true if matches user condition(meaning elem must be an object), false otherwise
  if(condition){
    console.log(parent[key],"will now be deleted")
    delete(parent[key]) //deletion example(if user conditions match)
  }
}
forEvery(testObj,testFn)
console.log("and the changed object looks like",testObj)

条件匹配

在此处输入图像描述


推荐阅读