简体   繁体   English

在嵌套的 json 对象中查找和更新

[英]Find and update in nested json object

I used this code to find the required portion from the json object from sJhonny's Question我使用此代码从sJhonny's Question的 json 对象中找到所需的部分

Data Sample数据样本

TestObj = {
    "Categories": [{
        "Products": [{
            "id": "a01",
            "name": "Pine",
            "description": "Short description of pine."
        },
        {
            "id": "a02",
            "name": "Birch",
            "description": "Short description of birch."
        },
        {
            "id": "a03",
            "name": "Poplar",
            "description": "Short description of poplar."
        }],
        "id": "A",
        "title": "Cheap",
        "description": "Short description of category A."
    },
    {
        "Product": [{
            "id": "b01",
            "name": "Maple",
            "description": "Short description of maple."
        },
        {
            "id": "b02",
            "name": "Oak",
            "description": "Short description of oak."
        },
        {
            "id": "b03",
            "name": "Bamboo",
            "description": "Short description of bamboo."
        }],
        "id": "B",
        "title": "Moderate",
        "description": "Short description of category B."
    }]
};

Function to find查找功能

function getObjects(obj, key, val) {
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(getObjects(obj[i], key, val));
        } else if (i == key && obj[key] == val) {
            objects.push(obj);
        }
    }
    return objects;
}

Use like so:像这样使用:

getObjects(TestObj, 'id', 'A'); // Returns an array of matching objects

This code is to select matching piece from the source.此代码用于从源中选择匹配的片段。 But what I want is to update the source object with new value and retrieve the updated source object.但我想要的是用新值更新源对象并检索更新的源对象。

I want something like我想要类似的东西

getObjects(TestObj, 'id', 'A', 'B'); // Returns source with updated value. (ie id:'A' updated to id:'B' in the returned object)

My code我的代码

function getObjects(obj, key, val, newVal) {
    var newValue = newVal;
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(getObjects(obj[i], key, val));
        } else if (i == key && obj[key] == val) {
            obj[key] = 'qwe';
        }
    }
    return obj;
}

This works if i give obj[key] = 'qwe';如果我给obj[key] = 'qwe';这会起作用obj[key] = 'qwe'; but if i change the code into obj[key] = newValue;但如果我将代码更改为obj[key] = newValue; its updated as undefined.其更新为未定义。

Why is that so?为什么?

You forgot to pass newValue in the nested call您忘记在嵌套调用中传递 newValue

function getObjects(obj, key, val, newVal) {
    var newValue = newVal;
    var objects = [];
    for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(getObjects(obj[i], key, val, newValue));
        } else if (i == key && obj[key] == val) {
            obj[key] = 'qwe';
        }
    }
    return obj;
}
function getObjects(obj, key, val, newVal) {
  for (var i in obj) {
      if (!obj.hasOwnProperty(i)) continue;
      if (i == key && obj[key] == val) {
          obj[key] = newVal;
      }
  }
  return obj
}

This will do the inplace update of a found value with the newValue (newVal)这将使用 newValue (newVal) 对找到的值进行就地更新

This ?这 ?

function update(obj, key, newVal) {
    for(var i in obj) {
        if(typeof obj[i] == 'object') {
            update(obj[i], key, newVal));
        } else if(i === key) {
            obj[i] = newVal;
        }
    }
    return obj;
}

you can try my solution你可以试试我的解决方案

const InsertOrUpdate = (dest, src) => {
    GetValue(dest, src, [])
}

const GetValue = (dest, src, keys) => {
    for (let key in src) {
        let srcValue = src[key]
        // Don't use push here
        // The concat() method does not change the existing arrays, but returns a new array, containing the values of the joined arrays
        let parentKeys = keys.concat(key)

        if (typeof (srcValue) === 'object') {
            GetValue(dest, srcValue, parentKeys)
        } else {
            SetValue(dest, parentKeys, srcValue)
        }
    }
}

const SetValue = (dest, keys, value) => {
    if (!keys.length || keys.length === 0) {
        return
    }

    let key = keys[0]
    let childKeys = keys.slice(1)

    // update
    // note: null is object
    if (dest[key] && typeof (dest[key]) === 'object') {
        SetValue(dest[key], childKeys, value)
    } else {
        // insert
        if (childKeys.length === 0) {
            dest[key] = value
        } else {
            // insert parent key & continue update
            dest[key] = {}
            SetValue(dest[key], childKeys, value)
        }
    }
}

I tried using the selected solution above, but it would update every row with the same value.我尝试使用上面选择的解决方案,但它会用相同的值更新每一行。 So I added a way to define what record you want to update, and also a way to keep track of the current record ID once you've already looped past it.因此,我添加了一种方法来定义您要更新的记录,以及一种在您循环过去后跟踪当前记录 ID 的方法。

function getObjects(obj, rowId, key, val, newVal, rId) {
    var objects = [];        
    for (var i in obj) {   
        if(obj[i].id !== undefined) rId = obj[i].id; 
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
            objects = objects.concat(this.updateObject(obj[i], rowId, key, val, newVal, rId));
        } else if (i == key && obj[key] == val) {
            if(rId == rowId) obj[key] = newVal;                      
        }
    }
    return obj;
}

Take a look at object-scan .看看对象扫描 We use it for a lot of data processing now.我们现在使用它进行大量数据处理。 For us it makes the code much more maintainable, just takes a moment to wrap your head around it.对我们来说,它使代码更易于维护,只需花一点时间来了解它。 Here is how you could answer your question这是你如何回答你的问题

 // const objectScan = require('object-scan'); const update = (data, needle, from, to) => objectScan([needle], { abort: true, rtn: 'bool', filterFn: ({ value, parent, property }) => { if (value === from) { parent[property] = to; return true; } return false; } })(data); // ------------------------------- const TestObj = { Categories: [{ Products: [{ id: 'a01', name: 'Pine', description: 'Short description of pine.' }, { id: 'a02', name: 'Birch', description: 'Short description of birch.' }, { id: 'a03', name: 'Poplar', description: 'Short description of poplar.' }], id: 'A', title: 'Cheap', description: 'Short description of category A.' }, { Product: [{ id: 'b01', name: 'Maple', description: 'Short description of maple.' }, { id: 'b02', name: 'Oak', description: 'Short description of oak.' }, { id: 'b03', name: 'Bamboo', description: 'Short description of bamboo.' }], id: 'B', title: 'Moderate', description: 'Short description of category B.' }] }; console.log(update(TestObj, '**.id', 'A', 'B')); // => true console.log(TestObj); // => { Categories: [ { Products: [ { id: 'a01', name: 'Pine', description: 'Short description of pine.' }, { id: 'a02', name: 'Birch', description: 'Short description of birch.' }, { id: 'a03', name: 'Poplar', description: 'Short description of poplar.' } ], id: 'B', title: 'Cheap', description: 'Short description of category A.' }, { Product: [ { id: 'b01', name: 'Maple', description: 'Short description of maple.' }, { id: 'b02', name: 'Oak', description: 'Short description of oak.' }, { id: 'b03', name: 'Bamboo', description: 'Short description of bamboo.' } ], id: 'B', title: 'Moderate', description: 'Short description of category B.' } ] }
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.8.0"></script>

Disclaimer : I'm the author of object-scan免责声明:我是对象扫描的作者

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM