简体   繁体   English

在javascript数组中查找和修改深度嵌套的对象

[英]find and modify deeply nested object in javascript array

I have an array of objects that can be of any length and any depth.我有一组可以是任意长度和任意深度的对象。 I need to be able to find an object by its id and then modify that object within the array.我需要能够通过其 id 找到一个对象,然后在数组中修改该对象。 Is there an efficient way to do this with either lodash or pure js?有没有一种有效的方法可以使用 lodash 或纯 js 来做到这一点?

I thought I could create an array of indexes that led to the object but constructing the expression to access the object with these indexes seems overly complex / unnecessary我以为我可以创建一个指向对象的索引数组,但是构造表达式以使用这些索引访问对象似乎过于复杂/不必要

edit1;编辑1; thanks for all yours replies I will try and be more specific.感谢您的所有回复,我会尝试更具体。 i am currently finding the location of the object I am trying to modify like so.我目前正在找到我试图像这样修改的对象的位置。 parents is an array of ids for each parent the target object has. parent 是目标对象具有的每个父级的 id 数组。 ancestors might be a better name for this array.祖先可能是这个数组的一个更好的名字。 costCenters is the array of objects that contains the object I want to modify. costCenters 是包含我要修改的对象的对象数组。 this function recurses and returns an array of indexes that lead to the object I want to modify此函数递归并返回指向我要修改的对象的索引数组

var findAncestorsIdxs = function(parents, costCenters, startingIdx, parentsIdxs) {
            var idx = startingIdx ? startingIdx : 0;
            var pidx = parentsIdxs ? parentsIdxs : [];

            _.each(costCenters, function(cc, ccIdx) {
                if(cc.id === parents[idx]) {
                    console.log(pidx);
                    idx = idx + 1;
                    pidx.push(ccIdx);
                    console.log(pidx);
                    pidx = findAncestorsIdx(parents, costCenters[ccIdx].children, idx, pidx);
                }
            });
            return pidx;
        }; 

Now with this array of indexes how do I target and modify the exact object I want?现在有了这个索引数组,我如何定位和修改我想要的确切对象? I have tried this where ancestors is the array of indexes, costCenters is the array with the object to be modified and parent is the new value to be assigned to the target object我试过这个,其中祖先是索引数组,costCenters 是包含要修改的对象的数组,父级是要分配给目标对象的新值

var setParentThroughAncestors = function(ancestors, costCenters, parent) {
            var ccs = costCenters;
            var depth = ancestors.length;
            var ancestor = costCenters[ancestors[0]];
            for(i = 1; i < depth; i++) {
                ancestor = ancestor.children[ancestors[i]];
            }
            ancestor = parent;
            console.log(ccs);
            return ccs;
        };

this is obviously just returning the unmodified costCenters array so the only other way I can see to target that object is to construct the expression like myObjects[idx1].children[2].grandchildren[3].ggranchildren[4].something = newValue.这显然只是返回未修改的 costCenters 数组,所以我能看到的唯一另一种方法是构建像 myObjects[idx1].children[2].grandchildren[3].ggranchildren[4].something = newValue 这样的表达式. is that the only way?这是唯一的方法吗? if so what is the best way to do that?如果是这样,最好的方法是什么?

You can use JSON.stringify for this.您可以为此使用JSON.stringify It provides a callback for each visited key/value pair (at any depth), with the ability to skip or replace.它为每个访问过的键/值对(在任何深度)提供回调,并具有跳过或替换的能力。

The function below returns a function which searches for objects with the specified ID and invokes the specified transform callback on them:下面的函数返回一个函数,该函数搜索具有指定 ID 的对象并对它们调用指定的转换回调:

function scan(id, transform) {
  return function(obj) {
    return JSON.parse(JSON.stringify(obj, function(key, value) {
      if (typeof value === 'object' && value !== null && value.id === id) {
        return transform(value);
      } else {
        return value;
      }
  }));
}

If as the problem is stated, you have an array of objects, and a parallel array of ids in each object whose containing objects are to be modified, and an array of transformation functions, then it's just a matter of wrapping the above as如果正如问题所陈述的那样,您有一个对象数组,每个包含要修改的对象的对象中都有一个并行的 id 数组,以及一个转换函数数组,那么只需将上述内容包装为

for (i = 0; i < objects.length; i++) {
    scan(ids[i], transforms[i])(objects[i]);
}

Due to restrictions on JSON.stringify, this approach will fail if there are circular references in the object, and omit functions, regexps, and symbol-keyed properties if you care.由于对 JSON.stringify 的限制,如果对象中存在循环引用,则此方法将失败,如果您关心,则省略函数、正则表达式和符号键属性。

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_native_JSON#The_replacer_parameter for more info.有关更多信息,请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_native_JSON#The_replacer_parameter

As Felix Kling said, you can iterate recursively over all objects.正如 Felix Kling 所说,您可以递归遍历所有对象。

// Overly-complex array
var myArray = {
    keyOne: {},
    keyTwo: {
        myId: {a: '3'}
    }
};
var searchId = 'myId', // Your search key
    foundValue, // Populated with the searched object
    found = false; // Internal flag for iterate()

// Recursive function searching through array
function iterate(haystack) {
    if (typeof haystack !== 'object' || haystack === null) return; // type-safety
    if (typeof haystack[searchId] !== 'undefined') {
        found = true;
        foundValue = haystack[searchId];
        return;
    } else {
        for (var i in haystack) {
            // avoid circular reference infinite loop & skip inherited properties
            if (haystack===haystack[i] || !haystack.hasOwnProperty(i)) continue;

            iterate(haystack[i]);
            if (found === true) return;
        }
    }
}

// USAGE / RESULT
iterate(myArray);
console.log(foundValue); // {a: '3'}
foundValue.b = 4; // Updating foundValue also updates myArray
console.log(myArray.keyTwo.myId); // {a: '3', b: 4}

All JS object assignations are passed as reference in JS.所有 JS 对象分配都作为 JS 中的引用传递。 See this for a complete tutorial on objects :)有关对象的完整教程,请参阅this :)

Edit: Thanks @torazaburo for suggestions for a better code.编辑:感谢@torazaburo 提供更好代码的建议。

If each object has property with the same name that stores other nested objects, you can use: https://github.com/dominik791/obj-traverse如果每个对象都具有存储其他嵌套对象的同名属性,则可以使用: https : //github.com/dominik791/obj-traverse

findAndModifyFirst() method should solve your problem. findAndModifyFirst()方法应该可以解决您的问题。 The first parameter is a root object, not array, so you should create it at first:第一个参数是一个根对象,而不是数组,所以你应该首先创建它:

var rootObj = {
  name: 'rootObject',
  children: [
    {
      'name': 'child1',
       children: [ ... ]
    },
    {
       'name': 'child2',
       children: [ ... ]
    }
  ]
};

Then use findAndModifyFirst() method:然后使用findAndModifyFirst()方法:

findAndModifyFirst(rootObj, 'children', { id: 1 }, replacementObject)

replacementObject is whatever object that should replace the object that has id equal to 1 . replacementObject是应该替换id等于1的对象的任何对象。

You can try it using demo app: https://dominik791.github.io/obj-traverse-demo/您可以使用演示应用程序进行尝试: https : //dominik791.github.io/obj-traverse-demo/

Here's an example that extensively uses lodash.这是一个广泛使用 lodash 的示例。 It enables you to transform a deeply nested value based on its key or its value.它使您能够根据其键或值转换深度嵌套的值。

const _ = require("lodash")
const flattenKeys = (obj, path = []) => (!_.isObject(obj) ? { [path.join('.')]: obj } : _.reduce(obj, (cum, next, key) => _.merge(cum, flattenKeys(next, [...path, key])), {}));


const registrations = [{
  key: "123",
  responses:
  {
    category: 'first',
  },
}]


function jsonTransform (json, conditionFn, modifyFn) {

  // transform { responses: { category: 'first' } } to { 'responses.category': 'first' }
  const flattenedKeys = Object.keys(flattenKeys(json));

  // Easily iterate over the flat json
  for(let i = 0; i < flattenedKeys.length; i++) {
    const key = flattenedKeys[i];
    const value = _.get(json, key)

    // Did the condition match the one we passed?
    if(conditionFn(key, value)) {

      // Replace the value to the new one    
      _.set(json, key, modifyFn(key, value)) 
    }
  }

  return json
}

// Let's transform all 'first' values to 'FIRST'
const modifiedCategory = jsonTransform(registrations, (key, value) => value === "first", (key, value) => value = value.toUpperCase())

console.log('modifiedCategory --', modifiedCategory)
// Outputs: modifiedCategory -- [ { key: '123', responses: { category: 'FIRST' } } ]

I needed to modify deeply nested objects too, and found no acceptable tool for that purpose.我也需要修改深度嵌套的对象,但没有找到可接受的工具。 Then I've made this and pushed it to npm.然后我做了这个并将它推送到 npm。

https://www.npmjs.com/package/find-and https://www.npmjs.com/package/find-and

This small [TypeScript-friendly] lib can help with modifying nested objects in a lodash manner.这个小的 [TypeScript-friendly] 库可以帮助以 lodash 方式修改嵌套对象。 Eg,例如,

var findAnd = require("find-and");

const data = {
  name: 'One',
  description: 'Description',
  children: [
    {
      id: 1,
      name: 'Two',
    },
    {
      id: 2,
      name: 'Three',
    },
  ],
};

findAnd.changeProps(data, { id: 2 }, { name: 'Foo' });

outputs产出

{
  name: 'One',
  description: 'Description',
  children: [
    {
      id: 1,
      name: 'Two',
    },
    {
      id: 2,
      name: 'Foo',
    },
  ],
}

https://runkit.com/embed/bn2hpyfex60e https://runkit.com/embed/bn2hpyfex60e

Hope this could help someone else.希望这可以帮助别人。

I wrote this code recently to do exactly this, as my backend is rails and wants keys like:我最近写了这段代码来做到这一点,因为我的后端是 rails 并且想要像这样的键:

first_name

and my front end is react, so keys are like:我的前端是反应,所以键是这样的:

firstName

And these keys are almost always deeply nested:这些键几乎总是深度嵌套的:

user: {
  firstName: "Bob",
  lastName: "Smith",
  email: "bob@email.com"
}

Becomes:变成:

user: {
  first_name: "Bob",
  last_name: "Smith",
  email: "bob@email.com"
}

Here is the code这是代码

function snakeCase(camelCase) {
  return camelCase.replace(/([A-Z])/g, "_$1").toLowerCase()
}

export function snakeCasedObj(obj) {
  return Object.keys(obj).reduce(
    (acc, key) => ({
      ...acc,
      [snakeCase(key)]: typeof obj[key] === "object" ? snakeCasedObj(obj[key]) : obj[key],
    }), {},
  );
}

Feel free to change the transform to whatever makes sense for you!随意将转换更改为对您有意义的任何内容!

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

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