按路径生成嵌套对象

发布于 2025-01-20 03:11:01 字数 263 浏览 0 评论 0原文

我想制作函数,例如

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

并返回嵌套对象:

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

最简单的方法是什么?

I want to make function which takes object path, for example

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

and returns nested object:

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

What's the most simple method to do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

遗失的美好 2025-01-27 03:11:01

如果您只需要执行单个键/值对,则使用splitreduceRightnest简单解决方案-

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"
        }
      }
    }
  }
}

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

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))

If you only need to do a single key/value pair, a straightforward solution of nest using split and reduceRight -

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"
        }
      }
    }
  }
}

If you need to apply this to an entire object where the key paths could possibly overlap with each other, we can write expand which depends on nest -

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
  }
}

We can write expand(o) to take Object.entries from the input object o and .map(nest) over each, and finally .reduce(merge) the result -

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))

虫児飞 2025-01-27 03:11:01

编辑:这是正确的答案,对不起,我第一次误读了它。

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);

旧答案读了错误的问题:

嘿,您可以使用以下代码来完成此操作。 It will only work with string values though with this implementation

"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);

You can see it working here

运行以上代码将登录该代码。

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

编辑:对不起,我知道您现在在JavaScript中要求使用此答案的答案是JS,但操场仍然是打字稿,但上面是JS。

EDIT: Here is the correct answer sorry I misread it the first time.

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);

OLD ANSWER THAT READ QUESTION WRONG:

Hey you can accomplish this with the below code. It will only work with string values though with this implementation

"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);

You can see it working here Typescript playground

Running the above code will log this to the console.

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

Edit: Sorry I realize now you asked for this in javascript updated the answer to be in JS but the playground is still typescript but the above is JS.

穿越时光隧道 2025-01-27 03:11:01

该版本的想法与Mulan的答案相似,但职责的分解不同。它通过我一直在调用pathentries的有用的中间格式,这与object.entries.entries输出的结果平行,但包含完整的路径,而不仅仅是顶级级别特性。对于此示例,它将只是

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

我们的展开函数调用object .Entries在您的输入上,将键分配在点上,然后调用Hydrate关于结果。 水合>水合 在我的常规腰带中,只是一个

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('。'。')。这并不困难,但会将我们带到更远的地方,就像处理更复杂的系统一样,该系统允许任意琴键,包括引号,括号,括号和时期。

This version has a similar idea to the answer from Mulan, but with a different breakdown in responsibilities. It goes through a useful intermediate format that I've been calling pathEntries, a parallel to the result of Object.entries output, but containing full paths rather than just top-level properties. For this example, it would be just

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

Our expand function calls Object .entries on your input, splits the keys on dots, and then calls hydrate on the result. hydrate is in my regular utility belt and is just a thin wrapper around setPath, folding the path entries into a single object. It looks like this:

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}

One useful thing about this intermediate format is that it can also represent arrays, using integers for array indices and strings for object properties:

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}}
// ]

But to use that feature here, we would probably need a more sophisticated string format, something like { '[0].foo.bar[0].qux': 42 } and we'd need a somewhat more sophisticated parser than just k .split ('.'). This is not difficult, but would take us further afield, as would dealing with the still more sophisticated system that allows for arbitrary string keys, including quotation marks, brackets, and periods.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文