我有一个对象数组,可以是任何长度和任何深度。我需要能够通过其id找到一个对象,然后在数组中修改该对象。有没有一种有效的方法可以用lodash或纯js来做到这一点?
我以为我可以创建一个导致该对象的索引数组,但构造表达式以使用这些索引访问该对象似乎过于复杂/不必要
EDIT1;感谢你的所有回复我会尝试更具体。我目前正在寻找我试图修改的对象的位置。 parents是目标对象拥有的每个父级的id数组。祖先可能是这个数组的更好名称。 costCenters是包含我要修改的对象的对象数组。这个函数recurses并返回一个索引数组,这些索引导致我想要修改的对象
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;
};
现在有了这个索引数组,我如何定位和修改我想要的确切对象?我试过这个,其中祖先是索引数组,costCenters是要修改对象的数组,parent是要分配给目标对象的新值
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;
};
这显然只是返回未修改的costCenters数组,所以我能看到的另一种方法就是构建像myObjects [idx1] .children [2] .grandchildren [3] .ggranchildren [4]这样的表达式。 something = newValue。这是唯一的方法吗?如果是这样,最好的方法是什么?
答案 0 :(得分:12)
您可以使用JSON.stringify
。它为每个访问的键/值对(在任何深度)提供回调,并具有跳过或替换的能力。
下面的函数返回一个函数,该函数搜索具有指定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;
}
}));
}
如果问题已经陈述,你有一个对象数组,每个对象的并行数组,其中包含要修改的对象,以及一个转换函数数组,那么它只是包装上面的问题如
for (i = 0; i < objects.length; i++) {
scan(ids[i], transforms[i])(objects[i]);
}
由于JSON.stringify的限制,如果对象中存在循环引用,则此方法将失败,并且如果您关心,则省略函数,正则表达式和符号键属性。
答案 1 :(得分:2)
正如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}
所有JS对象赋值都在JS中作为引用传递。有关对象的完整教程,请参阅this。)
编辑:感谢@torazaburo提供更好代码的建议。
答案 2 :(得分:1)
如果每个对象都具有存储其他嵌套对象的同名属性,则可以使用:https://github.com/dominik791/obj-traverse
findAndModifyFirst()
方法应该可以解决您的问题。第一个参数是根对象,而不是数组,因此您应该首先创建它:
var rootObj = {
name: 'rootObject',
children: [
{
'name': 'child1',
children: [ ... ]
},
{
'name': 'child2',
children: [ ... ]
}
]
};
然后使用findAndModifyFirst()
方法:
findAndModifyFirst(rootObj, 'children', { id: 1 }, replacementObject)
replacementObject
是应该替换id
等于1
的对象的任何对象。
您可以使用演示应用尝试: https://dominik791.github.io/obj-traverse-demo/
答案 3 :(得分:0)
这是一个广泛使用lodash的示例。它使您可以根据键或键的值来转换深层嵌套的值。
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' } } ]
答案 4 :(得分:0)
我也需要修改深层嵌套的对象,但没有找到可以接受的工具。然后,我将其制成并将其推送到npm。
https://www.npmjs.com/package/find-and
这个小巧的[TypeScript友好]库可以帮助以Lodash的方式修改嵌套对象。例如,
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' });
输出
{
name: 'One',
description: 'Description',
children: [
{
id: 1,
name: 'Two',
},
{
id: 2,
name: 'Foo',
},
],
}
https://runkit.com/embed/bn2hpyfex60e
希望这可以帮助其他人。
答案 5 :(得分:0)
我最近编写此代码是为了做到这一点,因为我的后端是rails并需要像这样的键:
first_name
并且我的前端在反应,所以按键就像:
firstName
这些键几乎总是嵌套在深处:
user: {
firstName: "Bob",
lastName: "Smith",
email: "bob@email.com"
}
成为:
user: {
first_name: "Bob",
last_name: "Smith",
email: "bob@email.com"
}
这是代码
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],
}), {},
);
}
随时将转换更改为对您有意义的任何内容!