按路径生成嵌套对象



我想制作一个采用对象路径的函数,例如

{ 'person.data.info.more.favorite': 'smth' }

并返回嵌套对象:

{
person: {
data: {
info: {
more: {
favorite: 'smth',
},
},
},
},
}

最简单的方法是什么?

如果您只需要执行单个键/值对,则使用splitreduceRight-的nest的直接解决方案

const nest = ([path, value]) =>
path.split('.').reduceRight((v, p) => ({ [p]: v }), value)
console.log(nest(['a', 'smth']))
console.log(nest(['a.b', 'smth']))
console.log(nest(['person.data.info.more.favorite', 'smth']))

{
"a": "smth"
}
{
"a": {
"b": "smth"
}
}
{
"person": {
"data": {
"info": {
"more": {
"favorite": "smth"
}
}
}
}
}

如果您需要将其应用于整个对象,其中关键路径可能相互重叠,我们可以编写依赖于nest-的expand

const input = {
"server.host": "localhost",
"server.port": 9999,
"database.host": "localhost",
"database.name": "mydb",
"database.port": 7777,
"database.user": "root",
"debug.log": true
}
console.log(expand(input))
{
"server": {
"host": "localhost",
"port": 9999
},
"database": {
"host": "localhost",
"name": "mydb",
"port": 7777,
"user": "root"
},
"debug": {
"log": true
}
}

我们可以写expand(o)从输入对象o.map(nest)中取Object.entries,最后.reduce(merge)取结果-

const expand = o =>
Object.entries(o).map(nest).reduce(merge, {})
const nest = ([path, value]) =>
path.split(".").reduceRight((v, p) => ({ [p]: v }), value)
const merge = (left = {}, right = {}) =>
Object
.entries(right)
.map(([ k, v ]) =>
isobject(v) && isobject(left[k])
? [ k, merge(left[k], v) ]
: [ k, v ]
)
.reduce(assign, left)

const assign = (o, [ k, v ]) =>
Object.assign(o, { [k]: v })
const isobject = t =>
t?.constructor === Object   

const input = {
"server.host": "localhost",
"server.port": 9999,
"database.host": "localhost",
"database.name": "mydb",
"database.port": 7777,
"database.user": "root",
"debug.log": true
}
console.log(expand(input))

EDIT:这是正确答案,对不起,我第一次看错了。

let myObj = { 'person.data.info.more.favorite': 'smth' }
function getNestedObj( object ) {
let keys = Object.keys( object )[0];
let value = Object.values( object )[0];
let keyArray = keys.split(".");
let nextObject;
let returnObj = {};

keyArray.forEach( ( key ) => {

let newObj = {};
if( !nextObject ) {
returnObj[key] = newObj
nextObject = newObj;
} else {
nextObject[key] = newObj
nextObject = newObj;
}
console.log(key)
});
nextObject[0] = value
console.log( returnObj );
return returnObj;
}
getNestedObj( myObj);

读错问题的旧答案:

嘿,你可以用下面的代码来完成这个。它只适用于字符串值,尽管使用此实现

"use strict";
let myObject = {
person: {
data: {
info: {
more: {
favorite: 'smth',
},
less: {
favourite: 'smthing else'
},
random: "a string"
},
},
},
};
function concatKeys(object, key) {
let keys = Object.keys(object);
let keyDictionary = {};
if (key && typeof object === 'string') {
return { [key]: object };
}
if (keys.length > 0) {
keys.forEach((nextKey) => {
let newKey = key ? `${key}.${nextKey}` : nextKey;
let nextObj = object[nextKey];
let newDictionary = concatKeys(nextObj, newKey);
keyDictionary = Object.assign(Object.assign({}, keyDictionary), newDictionary);
});
}
return keyDictionary;
}
let output = concatKeys(myObject);
console.log(output);

你可以看到它在这里工作打字操场

运行上面的代码会将其记录到控制台。

[LOG]: {
"person.data.info.more.favorite": "smth",
"person.data.info.less.favourite": "smthing else",
"person.data.info.random": "a string"
} 

编辑:对不起,我现在意识到你在javascript中要求这个更新的答案是在JS中,但操场仍然是typescript,但上面是JS。

这个版本的想法与《花木兰》中的答案相似,但职责不同。它通过了一个有用的中间格式,我一直在调用pathEntries,它与Object.entries输出的结果并行,但包含完整路径,而不仅仅是顶级属性。对于这个例子,它将只是

[['person', 'data', 'info', 'more', 'favorite'], 'smth']

我们的expand函数对您的输入调用Object .entries,将键拆分为点,然后对结果调用hydratehydrate在我的常规实用皮带中,只是setPath的一个薄薄的包装,将路径条目折叠成一个对象。它看起来像这样:

const setPath = ([p, ...ps]) => (v) => (o) =>
p == undefined ? v : Object .assign (
Array .isArray (o) || Number .isInteger (p) ? [] : {},
{...o, [p]: setPath (ps) (v) ((o || {}) [p])}
)
const hydrate = (xs) =>
xs .reduce ((a, [p, v]) => setPath (p) (v) (a), {})
const expand  = (o) =>
hydrate (Object .entries (o) .map (([k, v]) => [k.split ('.'), v]))
console .log (expand ({'person.data.info.more.favorite': 'smth'}))
const input = {"server.host": "localhost", "server.port": 9999, "database.host": "localhost", "database.name": "mydb", "database.port": 7777, "database.user": "root", "debug.log": true}
console .log (expand (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

这种中间格式的一个有用之处是,它还可以表示数组,使用整数表示数组索引,使用字符串表示对象属性:

hydrate ([
[[0, 'foo', 'bar', 0, 'qux'], 42], 
[[0, 'foo', 'bar', 1, 'qux'], 43], 
[[0, 'foo', 'baz'], 6],
[[1, 'foo', 'bar', 0, 'qux'], 44], 
[[1, 'foo', 'bar', 1, 'qux'], 45], 
[[1, 'foo', 'baz'], 7],  
]) //=> 
// [
//   {foo: {bar: [{qux: 42}, {qux: 43}], baz: 6}}, 
//   {foo: {bar: [{qux: 44}, {qux: 45}], baz: 7}}
// ]

但要在这里使用该功能,我们可能需要一个更复杂的字符串格式,比如{ '[0].foo.bar[0].qux': 42 },并且我们需要一个比k .split ('.')更复杂的解析器。这并不困难,但会让我们走得更远,就像处理更复杂的系统一样,该系统允许使用任意字符串键,包括引号、括号和句点。

最新更新