Javascript查找大对象中的嵌套键

3

我想获取嵌套在这个JSON对象中的eth0的值。

"addresses": {
  "eth0": [
    "10.0.3.188"
  ]
},

我使用underscore.js来简化这个过程

var _ = require('underscore')._;

var jsonData = {
    "plays": [{
        "play": {
            "id": "d10aae34-6713-4e14-8ad5-fa2fbf6aa2b5",
            "name": "lxc"
        },
        "tasks": [{
                "hosts": {
                    "lxc.server.com": {
                        "_ansible_no_log": false,
                        "_ansible_parsed": true,
                        "changed": true,
                        "cmd": "lxc copy base \"bar69\"",
                        "delta": "0:00:01.417533",
                        "end": "2017-01-10 18:01:28.692981",
                        "invocation": {
                            "module_args": {
                                "_raw_params": "lxc copy base \"bar69\"",
                                "_uses_shell": true,
                                "chdir": null,
                                "creates": null,
                                "executable": null,
                                "removes": null,
                                "warn": true
                            },
                            "module_name": "command"
                        },
                        "rc": 0,
                        "start": "2017-01-10 18:01:27.275448",
                        "stderr": "",
                        "stdout": "",
                        "stdout_lines": [],
                        "warnings": []
                    }
                },
                "task": {
                    "id": "297bf7b7-9ee7-4517-8763-bc3b15baa6e2",
                    "name": "clone from base"
                }
            },
            {
                "hosts": {
                    "lxc.server.com": {
                        "_ansible_no_log": false,
                        "_ansible_parsed": true,
                        "changed": true,
                        "cmd": "lxc config set \"bar69\" security.privileged true",
                        "delta": "0:00:00.053403",
                        "end": "2017-01-10 18:01:32.270750",
                        "invocation": {
                            "module_args": {
                                "_raw_params": "lxc config set \"bar69\" security.privileged true",
                                "_uses_shell": true,
                                "chdir": null,
                                "creates": null,
                                "executable": null,
                                "removes": null,
                                "warn": true
                            },
                            "module_name": "command"
                        },
                        "rc": 0,
                        "start": "2017-01-10 18:01:32.217347",
                        "stderr": "",
                        "stdout": "",
                        "stdout_lines": [],
                        "warnings": []
                    }
                },
                "task": {
                    "id": "bc63ad6f-1808-48b8-a1de-729153d2b0c5",
                    "name": "Promote to privileged ct"
                }
            },
            {
                "hosts": {
                    "lxc.server.com": {
                        "_ansible_no_log": false,
                        "_ansible_parsed": true,
                        "actions": [
                            "start"
                        ],
                        "addresses": {
                            "eth0": [
                                "10.0.3.188"
                            ]
                        },
                        "changed": true,
                        "invocation": {
                            "module_args": {
                                "architecture": null,
                                "cert_file": "/root/.config/lxc/client.crt",
                                "config": null,
                                "description": null,
                                "devices": null,
                                "ephemeral": null,
                                "force_stop": false,
                                "key_file": "/root/.config/lxc/client.key",
                                "name": "bar69",
                                "profiles": null,
                                "source": null,
                                "state": "started",
                                "timeout": 30,
                                "trust_password": null,
                                "url": "unix:/var/lib/lxd/unix.socket",
                                "wait_for_ipv4_addresses": true
                            },
                            "module_name": "lxd_container"
                        },
                        "log_verbosity": 0,
                        "old_state": "stopped"
                    }
                },
                "task": {
                    "id": "466c0da9-6cbf-4196-aea9-109218c3ed5f",
                    "name": "Start CT"
                }
            },
            {
                "hosts": {
                    "lxc.server.com": {
                        "_ansible_no_log": false,
                        "_ansible_verbose_always": true,
                        "changed": false,
                        "invocation": {
                            "module_args": {
                                "msg": [
                                    "10.0.3.188"
                                ]
                            },
                            "module_name": "debug"
                        },
                        "msg": [
                            "10.0.3.188"
                        ]
                    }
                },
                "task": {
                    "id": "978c490e-59c3-41d2-818d-ab4b557ad803",
                    "name": ""
                }
            }
        ]
    }],
    "stats": {
        "lxc.server.com": {
            "changed": 3,
            "failures": 0,
            "ok": 4,
            "skipped": 0,
            "unreachable": 0
        }
    }
}

这是我迄今为止尝试过的,但没有成功!
console.log(_.findKey(_.values(jsonData.tasks)));

非常感谢您的帮助


不是每个主机都会有这个值? - Ajay Narain Mathur
不,不是每个主机。 - Deano
5个回答

2
您可以不使用下划线来完成此操作。一种方法是获取给定的属性字符串和任务,基于分隔符拆分属性(您不能使用'.',因为您有像'lxc.server.com'这样的点属性),并递归地检查对象,直到找到值(或未找到)。
注意:以下解决方案假定您要传递一个带有限定符的属性字符串,但您同样可以直接传递属性数组而不包装辅助函数。
尾部递归解决方案

var jsonData={plays:[{play:{id:"d10aae34-6713-4e14-8ad5-fa2fbf6aa2b5",name:"lxc"},tasks:[{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,changed:!0,cmd:'lxc copy base "bar69"',delta:"0:00:01.417533",end:"2017-01-10 18:01:28.692981",invocation:{module_args:{_raw_params:'lxc copy base "bar69"',_uses_shell:!0,chdir:null,creates:null,executable:null,removes:null,warn:!0},module_name:"command"},rc:0,start:"2017-01-10 18:01:27.275448",stderr:"",stdout:"",stdout_lines:[],warnings:[]}},task:{id:"297bf7b7-9ee7-4517-8763-bc3b15baa6e2",name:"clone from base"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,changed:!0,cmd:'lxc config set "bar69" security.privileged true',delta:"0:00:00.053403",end:"2017-01-10 18:01:32.270750",invocation:{module_args:{_raw_params:'lxc config set "bar69" security.privileged true',_uses_shell:!0,chdir:null,creates:null,executable:null,removes:null,warn:!0},module_name:"command"},rc:0,start:"2017-01-10 18:01:32.217347",stderr:"",stdout:"",stdout_lines:[],warnings:[]}},task:{id:"bc63ad6f-1808-48b8-a1de-729153d2b0c5",name:"Promote to privileged ct"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,actions:["start"],addresses:{eth0:["10.0.3.188"]},changed:!0,invocation:{module_args:{architecture:null,cert_file:"/root/.config/lxc/client.crt",config:null,description:null,devices:null,ephemeral:null,force_stop:!1,key_file:"/root/.config/lxc/client.key",name:"bar69",profiles:null,source:null,state:"started",timeout:30,trust_password:null,url:"unix:/var/lib/lxd/unix.socket",wait_for_ipv4_addresses:!0},module_name:"lxd_container"},log_verbosity:0,old_state:"stopped"}},task:{id:"466c0da9-6cbf-4196-aea9-109218c3ed5f",name:"Start CT"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_verbose_always:!0,changed:!1,invocation:{module_args:{msg:["10.0.3.188"]},module_name:"debug"},msg:["10.0.3.188"]}},task:{id:"978c490e-59c3-41d2-818d-ab4b557ad803",name:""}}]}],stats:{"lxc.server.com":{changed:3,failures:0,ok:4,skipped:0,unreachable:0}}};
  
function getNestedPropHelper(obj, [first, ...rest]) {
  // base case
  if (typeof obj !== 'object' || !obj) return undefined;
 
  return rest.length === 0                   // if we only have one property
    ? obj[first]                             // return the value
    : getNestedPropHelper(obj[first], rest); // otherwise recursively return the rest
}

function getNestedProp(obj, prop, delim = '|') {
  return getNestedPropHelper(obj, prop.split(delim));
}

// extract the tasks
const tasks = jsonData.plays.reduce((arr, play) => arr.concat(play.tasks), []);

// get the eth0 property for each task
const props = tasks.map(task => 
  getNestedProp(task, 'hosts|lxc.server.com|addresses|eth0')
);

// log eth0 properties for each task (only the third one actually has the value)
console.log(props);

你还可以采用迭代的方式来实现,这通常会更快(尽管在支持尾调用的环境中速度提升不大)。 迭代解决方案

var jsonData={plays:[{play:{id:"d10aae34-6713-4e14-8ad5-fa2fbf6aa2b5",name:"lxc"},tasks:[{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,changed:!0,cmd:'lxc copy base "bar69"',delta:"0:00:01.417533",end:"2017-01-10 18:01:28.692981",invocation:{module_args:{_raw_params:'lxc copy base "bar69"',_uses_shell:!0,chdir:null,creates:null,executable:null,removes:null,warn:!0},module_name:"command"},rc:0,start:"2017-01-10 18:01:27.275448",stderr:"",stdout:"",stdout_lines:[],warnings:[]}},task:{id:"297bf7b7-9ee7-4517-8763-bc3b15baa6e2",name:"clone from base"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,changed:!0,cmd:'lxc config set "bar69" security.privileged true',delta:"0:00:00.053403",end:"2017-01-10 18:01:32.270750",invocation:{module_args:{_raw_params:'lxc config set "bar69" security.privileged true',_uses_shell:!0,chdir:null,creates:null,executable:null,removes:null,warn:!0},module_name:"command"},rc:0,start:"2017-01-10 18:01:32.217347",stderr:"",stdout:"",stdout_lines:[],warnings:[]}},task:{id:"bc63ad6f-1808-48b8-a1de-729153d2b0c5",name:"Promote to privileged ct"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,actions:["start"],addresses:{eth0:["10.0.3.188"]},changed:!0,invocation:{module_args:{architecture:null,cert_file:"/root/.config/lxc/client.crt",config:null,description:null,devices:null,ephemeral:null,force_stop:!1,key_file:"/root/.config/lxc/client.key",name:"bar69",profiles:null,source:null,state:"started",timeout:30,trust_password:null,url:"unix:/var/lib/lxd/unix.socket",wait_for_ipv4_addresses:!0},module_name:"lxd_container"},log_verbosity:0,old_state:"stopped"}},task:{id:"466c0da9-6cbf-4196-aea9-109218c3ed5f",name:"Start CT"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_verbose_always:!0,changed:!1,invocation:{module_args:{msg:["10.0.3.188"]},module_name:"debug"},msg:["10.0.3.188"]}},task:{id:"978c490e-59c3-41d2-818d-ab4b557ad803",name:""}}]}],stats:{"lxc.server.com":{changed:3,failures:0,ok:4,skipped:0,unreachable:0}}};
  
function getNestedPropHelper(obj, props) {
  const isObject = (obj) => typeof obj === 'object' && obj;
  if (!isObject(obj)) return undefined;

  // keep extracting the properties
  for (const prop of props) {
    obj = obj[prop];
    // of we come across a non-object property before we're done, the property path is invalid
    if (!isObject(obj)) return undefined;
  }

  // if we reached this point, we found the value
  return obj;
}

function getNestedProp(obj, prop, delim = '|') {
  return getNestedPropHelper(obj, prop.split(delim));
}

// extract the tasks
const tasks = jsonData.plays.reduce((arr, play) => arr.concat(play.tasks), []);

// get the eth0 property for each task
const props = tasks.map(task => 
  getNestedProp(task, 'hosts|lxc.server.com|addresses|eth0')
);

// log eth0 properties for each task (only the third one actually has the value)
console.log(props);

柯里化递归解决方案 迭代解决方案也可以采用同样的方法

您还可以柯里化函数,以便为特定的属性路径构建Getter。

var jsonData={plays:[{play:{id:"d10aae34-6713-4e14-8ad5-fa2fbf6aa2b5",name:"lxc"},tasks:[{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,changed:!0,cmd:'lxc copy base "bar69"',delta:"0:00:01.417533",end:"2017-01-10 18:01:28.692981",invocation:{module_args:{_raw_params:'lxc copy base "bar69"',_uses_shell:!0,chdir:null,creates:null,executable:null,removes:null,warn:!0},module_name:"command"},rc:0,start:"2017-01-10 18:01:27.275448",stderr:"",stdout:"",stdout_lines:[],warnings:[]}},task:{id:"297bf7b7-9ee7-4517-8763-bc3b15baa6e2",name:"clone from base"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,changed:!0,cmd:'lxc config set "bar69" security.privileged true',delta:"0:00:00.053403",end:"2017-01-10 18:01:32.270750",invocation:{module_args:{_raw_params:'lxc config set "bar69" security.privileged true',_uses_shell:!0,chdir:null,creates:null,executable:null,removes:null,warn:!0},module_name:"command"},rc:0,start:"2017-01-10 18:01:32.217347",stderr:"",stdout:"",stdout_lines:[],warnings:[]}},task:{id:"bc63ad6f-1808-48b8-a1de-729153d2b0c5",name:"Promote to privileged ct"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_parsed:!0,actions:["start"],addresses:{eth0:["10.0.3.188"]},changed:!0,invocation:{module_args:{architecture:null,cert_file:"/root/.config/lxc/client.crt",config:null,description:null,devices:null,ephemeral:null,force_stop:!1,key_file:"/root/.config/lxc/client.key",name:"bar69",profiles:null,source:null,state:"started",timeout:30,trust_password:null,url:"unix:/var/lib/lxd/unix.socket",wait_for_ipv4_addresses:!0},module_name:"lxd_container"},log_verbosity:0,old_state:"stopped"}},task:{id:"466c0da9-6cbf-4196-aea9-109218c3ed5f",name:"Start CT"}},{hosts:{"lxc.server.com":{_ansible_no_log:!1,_ansible_verbose_always:!0,changed:!1,invocation:{module_args:{msg:["10.0.3.188"]},module_name:"debug"},msg:["10.0.3.188"]}},task:{id:"978c490e-59c3-41d2-818d-ab4b557ad803",name:""}}]}],stats:{"lxc.server.com":{changed:3,failures:0,ok:4,skipped:0,unreachable:0}}};
  
function getNestedPropHelper(obj, [first, ...rest]) {
  // base case
  if (typeof obj !== 'object' || !obj) return undefined;
 
  return rest.length === 0                   // if we only have one property
    ? obj[first]                             // return the value
    : getNestedPropHelper(obj[first], rest); // otherwise recursively return the rest
}

function getNestedProp(prop, delim = '|') {
  const props = prop.split(delim);
  return function(obj) {
    return getNestedPropHelper(obj, props);
  }
}

// now you have a getter that will extract eth0 for any task
const getEth0 = getNestedProp('hosts|lxc.server.com|addresses|eth0');

// extract the tasks
const tasks = jsonData.plays.reduce((arr, play) => arr.concat(play.tasks), []);

// extract eth0 from each task
const props = tasks.map(getEth0);

// log eth0 properties for each task (only the third one actually has the value)        
console.log(props);


很高兴能帮助你,伙计 :) - nem035

2
您可以使用一些函数,例如_.pluck(),以获取键为host的对象,_.property()来查看每个主机对象是否具有键addresses.map()以获取值的映射,._filter()以查看是否从映射中返回了值等等。
var hosts = _.pluck(jsonData.plays[0].tasks, 'hosts');
var mapping = _.map(hosts, function(host) {
    var keys = _.keys(host);
    if (_.size(keys)) {
      var nestedHost = host[_.first(keys)];
      if (_.property('addresses')(nestedHost)) {
        if (_.property('eth0')(nestedHost.addresses)) {
          return nestedHost.addresses.eth0[0];
        }
      }
    }
  });
console.log(_.filter(mapping));

此 Plunker中,以及下面的示例中查看其实际应用:

var jsonData = {
  "plays": [{
    "play": {
      "id": "d10aae34-6713-4e14-8ad5-fa2fbf6aa2b5",
      "name": "lxc"
    },
    "tasks": [{
      "hosts": {
        "lxc.server.com": {
          "_ansible_no_log": false,
          "_ansible_parsed": true,
          "changed": true,
          "cmd": "lxc copy base \"bar69\"",
          "delta": "0:00:01.417533",
          "end": "2017-01-10 18:01:28.692981",
          "invocation": {
            "module_args": {
              "_raw_params": "lxc copy base \"bar69\"",
              "_uses_shell": true,
              "chdir": null,
              "creates": null,
              "executable": null,
              "removes": null,
              "warn": true
            },
            "module_name": "command"
          },
          "rc": 0,
          "start": "2017-01-10 18:01:27.275448",
          "stderr": "",
          "stdout": "",
          "stdout_lines": [],
          "warnings": []
        }
      },
      "task": {
        "id": "297bf7b7-9ee7-4517-8763-bc3b15baa6e2",
        "name": "clone from base"
      }
    }, {
      "hosts": {
        "lxc.server.com": {
          "_ansible_no_log": false,
          "_ansible_parsed": true,
          "changed": true,
          "cmd": "lxc config set \"bar69\" security.privileged true",
          "delta": "0:00:00.053403",
          "end": "2017-01-10 18:01:32.270750",
          "invocation": {
            "module_args": {
              "_raw_params": "lxc config set \"bar69\" security.privileged true",
              "_uses_shell": true,
              "chdir": null,
              "creates": null,
              "executable": null,
              "removes": null,
              "warn": true
            },
            "module_name": "command"
          },
          "rc": 0,
          "start": "2017-01-10 18:01:32.217347",
          "stderr": "",
          "stdout": "",
          "stdout_lines": [],
          "warnings": []
        }
      },
      "task": {
        "id": "bc63ad6f-1808-48b8-a1de-729153d2b0c5",
        "name": "Promote to privileged ct"
      }
    }, {
      "hosts": {
        "lxc.server.com": {
          "_ansible_no_log": false,
          "_ansible_parsed": true,
          "actions": [
            "start"
          ],
          "addresses": {
            "eth0": [
              "10.0.3.188"
            ]
          },
          "changed": true,
          "invocation": {
            "module_args": {
              "architecture": null,
              "cert_file": "/root/.config/lxc/client.crt",
              "config": null,
              "description": null,
              "devices": null,
              "ephemeral": null,
              "force_stop": false,
              "key_file": "/root/.config/lxc/client.key",
              "name": "bar69",
              "profiles": null,
              "source": null,
              "state": "started",
              "timeout": 30,
              "trust_password": null,
              "url": "unix:/var/lib/lxd/unix.socket",
              "wait_for_ipv4_addresses": true
            },
            "module_name": "lxd_container"
          },
          "log_verbosity": 0,
          "old_state": "stopped"
        }
      },
      "task": {
        "id": "466c0da9-6cbf-4196-aea9-109218c3ed5f",
        "name": "Start CT"
      }
    }, {
      "hosts": {
        "lxc.server.com": {
          "_ansible_no_log": false,
          "_ansible_verbose_always": true,
          "changed": false,
          "invocation": {
            "module_args": {
              "msg": [
                "10.0.3.188"
              ]
            },
            "module_name": "debug"
          },
          "msg": [
            "10.0.3.188"
          ]
        }
      },
      "task": {
        "id": "978c490e-59c3-41d2-818d-ab4b557ad803",
        "name": ""
      }
    }]
  }],
  "stats": {
    "lxc.server.com": {
      "changed": 3,
      "failures": 0,
      "ok": 4,
      "skipped": 0,
      "unreachable": 0
    }
  }
};
document.addEventListener('DOMContentLoaded', function() {
  var hosts = _.pluck(jsonData.plays[0].tasks, 'hosts');
  var mapping = _.map(hosts, function(host) {
    var keys = _.keys(host);
    if (_.size(keys)) {
      var nestedHost = host[_.first(keys)];
      if (_.property('addresses')(nestedHost)) {
        if (_.property('eth0')(nestedHost.addresses)) {
          return nestedHost.addresses.eth0[0];
        }
      }
    }
  });
  document.getElementById('console').innerHTML = _.filter(mapping);
});
<script data-require="underscore.js@*" data-semver="1.8.3" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Address(es):
<div id="console"></div>


1
我更新了它,使用_.size() - 我们希望在获取键集合的第一个元素之前确保键数组具有元素。在这种情况下,总是存在键,但如果主机是一个空对象(即'hosts': {}),则可能不存在。 - Sᴀᴍ Onᴇᴌᴀ

1
如果您想直接访问它,请尝试以下操作:

jsonData.plays[0].tasks[2].hosts["lxc.server.com"].addresses.eth0[0]

编辑:已经测试过了。包含tasks[3]的帖子是错误的。

0

我们在许多数据处理任务中使用object-scan。一旦你理解了它,它就非常强大和快速。以下是如何解决你的问题:

// const objectScan = require('object-scan');

const find = (input) => objectScan(['**.addresses.eth0[0]'], {
  abort: true,
  rtn: 'value'
})(input);

const jsonData = { plays: [{ play: { id: 'd10aae34-6713-4e14-8ad5-fa2fbf6aa2b5', name: 'lxc' }, tasks: [{ hosts: { 'lxc.server.com': { _ansible_no_log: false, _ansible_parsed: true, changed: true, cmd: 'lxc copy base "bar69"', delta: '0:00:01.417533', end: '2017-01-10 18:01:28.692981', invocation: { module_args: { _raw_params: 'lxc copy base "bar69"', _uses_shell: true, chdir: null, creates: null, executable: null, removes: null, warn: true }, module_name: 'command' }, rc: 0, start: '2017-01-10 18:01:27.275448', stderr: '', stdout: '', stdout_lines: [], warnings: [] } }, task: { id: '297bf7b7-9ee7-4517-8763-bc3b15baa6e2', name: 'clone from base' } }, { hosts: { 'lxc.server.com': { _ansible_no_log: false, _ansible_parsed: true, changed: true, cmd: 'lxc config set "bar69" security.privileged true', delta: '0:00:00.053403', end: '2017-01-10 18:01:32.270750', invocation: { module_args: { _raw_params: 'lxc config set "bar69" security.privileged true', _uses_shell: true, chdir: null, creates: null, executable: null, removes: null, warn: true }, module_name: 'command' }, rc: 0, start: '2017-01-10 18:01:32.217347', stderr: '', stdout: '', stdout_lines: [], warnings: [] } }, task: { id: 'bc63ad6f-1808-48b8-a1de-729153d2b0c5', name: 'Promote to privileged ct' } }, { hosts: { 'lxc.server.com': { _ansible_no_log: false, _ansible_parsed: true, actions: ['start'], addresses: { eth0: ['10.0.3.188'] }, changed: true, invocation: { module_args: { architecture: null, cert_file: '/root/.config/lxc/client.crt', config: null, description: null, devices: null, ephemeral: null, force_stop: false, key_file: '/root/.config/lxc/client.key', name: 'bar69', profiles: null, source: null, state: 'started', timeout: 30, trust_password: null, url: 'unix:/var/lib/lxd/unix.socket', wait_for_ipv4_addresses: true }, module_name: 'lxd_container' }, log_verbosity: 0, old_state: 'stopped' } }, task: { id: '466c0da9-6cbf-4196-aea9-109218c3ed5f', name: 'Start CT' } }, { hosts: { 'lxc.server.com': { _ansible_no_log: false, _ansible_verbose_always: true, changed: false, invocation: { module_args: { msg: ['10.0.3.188'] }, module_name: 'debug' }, msg: ['10.0.3.188'] } }, task: { id: '978c490e-59c3-41d2-818d-ab4b557ad803', name: '' } }] }], stats: { 'lxc.server.com': { changed: 3, failures: 0, ok: 4, skipped: 0, unreachable: 0 } } };

console.log(find(jsonData));
// => 10.0.3.188
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>

声明:本人是object-scan的作者。


-1

你可以使用这段代码来循环遍历对象属性。

   for (key in jsonData){
       if(key == "eth0" ){
          console.log(jsonData[key].toString())
       }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接