在嵌套数组中按键查找


74

假设我有一个对象:

[
    {
        'title': "some title"
        'channel_id':'123we'
        'options': [
                    {
                'channel_id':'abc'
                'image':'http://asdasd.com/all-inclusive-block-img.jpg'
                'title':'All-Inclusive'
                'options':[
                    {
                        'channel_id':'dsa2'
                        'title':'Some Recommends'
                        'options':[
                            {
                                'image':'http://www.asdasd.com'                                 'title':'Sandals'
                                'id':'1'
                                'content':{
                                     ...

我想找到一个id为1的对象。是否有类似这样的功能?我可以使用Underscore的_.filter方法,但是我必须从顶部开始然后向下过滤。

Answers:


88

递归是您的朋友。我更新了该函数以说明属性数组:

function getObject(theObject) {
    var result = null;
    if(theObject instanceof Array) {
        for(var i = 0; i < theObject.length; i++) {
            result = getObject(theObject[i]);
            if (result) {
                break;
            }   
        }
    }
    else
    {
        for(var prop in theObject) {
            console.log(prop + ': ' + theObject[prop]);
            if(prop == 'id') {
                if(theObject[prop] == 1) {
                    return theObject;
                }
            }
            if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
                result = getObject(theObject[prop]);
                if (result) {
                    break;
                }
            } 
        }
    }
    return result;
}

更新的jsFiddle:http : //jsfiddle.net/FM3qu/7/


1
但是,等等,看一下我的代码,选项可能有很多对象[{},{}]。这将如何工作?
哈里

那里。我之前提到过,需要对其进行更新以解决数组问题,因此您可以开始使用。
Zach 2013年

如果选项中有多个对象,则此方法不起作用
哈利

1
固定。我添加了结果变量和else逻辑。更新后的jsFiddle显示它可以正常工作。
Zach

1
Zack我稍微修改了您的代码,现在可以正常使用了。非常感谢!看来我目前无法访问jsfiddle。工程生效后,我会分享我的更改
哈里

25

对我有用的是这种懒惰的方法,而不是算法上的懒惰;)

if( JSON.stringify(object_name).indexOf("key_name") > -1 ) {
    console.log("Key Found");
}
else{
    console.log("Key not Found");
}

您如何使用它if( JSON.stringify(object_name).indexOf('{"key_name":') > -1 ) {呢?包括左花括号,双引号和JSON.stringify添加的冒号?我认为这可能是一个很好的双重检查,以确保key_name实际上是键,而不是“值”部分中的键。
edwardsmarkf

2
但是,潜在的良好测试失败了,该测试将key_name放在某个值中。也不会返回包含id的对象
-Ranga

@Ranga ...同意,此解决方案仅在您仅需检查对象中是否存在键的情况下起作用,前提是该键在其中一个值中不存在key_name ....尽管我们可以修改代码如果必须确保它仅出现在键中,则在indexOf函数内的键名周围包括“”。(再次,在键名中带有“”的键名不应出现在值中
。– abhinav1602

1
我喜欢它!JSON.stringify(object).includes(text)
Marcelo Pereira Rodrigues

19

如果要在搜索对象时获取ID为1的第一个元素,则可以使用以下函数:

function customFilter(object){
    if(object.hasOwnProperty('id') && object["id"] == 1)
        return object;

    for(var i=0; i<Object.keys(object).length; i++){
        if(typeof object[Object.keys(object)[i]] == "object"){
            var o = customFilter(object[Object.keys(object)[i]]);
            if(o != null)
                return o;
        }
    }

    return null;
}

如果要获取所有id为1的元素,那么(所有id为1的元素都存储在结果中):

function customFilter(object, result){
    if(object.hasOwnProperty('id') && object.id == 1)
        result.push(object);

    for(var i=0; i<Object.keys(object).length; i++){
        if(typeof object[Object.keys(object)[i]] == "object"){
            customFilter(object[Object.keys(object)[i]], result);
        }
    }
}

1
您的解决方案比@regularmike建议的解决方案更好,因为我可以通过添加键和值搜索来自定义它。它对您有用,对他不起作用。 jsfiddle.net/76fp54xt 工作示例
Iulian Pinzaru,

7

另一个(有点愚蠢)的选择是利用的自然递归性质JSON.stringify,并向其传递一个替换函数,该函数在字符串化过程中在每个嵌套对象上运行:

const input = [{
  'title': "some title",
  'channel_id': '123we',
  'options': [{
    'channel_id': 'abc',
    'image': 'http://asdasd.com/all-inclusive-block-img.jpg',
    'title': 'All-Inclusive',
    'options': [{
      'channel_id': 'dsa2',
      'title': 'Some Recommends',
      'options': [{
        'image': 'http://www.asdasd.com',
        'title': 'Sandals',
        'id': '1',
        'content': {}
      }]
    }]
  }]
}];

console.log(findNestedObj(input, 'id', '1'));

function findNestedObj(entireObj, keyToFind, valToFind) {
  let foundObj;
  JSON.stringify(input, (_, nestedValue) => {
    if (nestedValue && nestedValue[keyToFind] === valToFind) {
      foundObj = nestedValue;
    }
    return nestedValue;
  });
  return foundObj;
};


这真的很聪明
Claudiu

1
我喜欢您的想法:)我只是认为,如果我们已经找到结果,那么如果中断递归地运行replacer函数,它将看起来更好。例如,我们可以使用throwjsfiddle.net/w4vs36hm
Max Martynov

6

我通过谷歌搜索找到了相似功能的页面。根据Zach和Regularmike提供的工作,我创建了另一个适合我需要的版本。
顺便说一句,辛苦的工作扎和正规军!我将代码发布在这里:

function findObjects(obj, targetProp, targetValue, finalResults) {

  function getObject(theObject) {
    let result = null;
    if (theObject instanceof Array) {
      for (let i = 0; i < theObject.length; i++) {
        getObject(theObject[i]);
      }
    }
    else {
      for (let prop in theObject) {
        if(theObject.hasOwnProperty(prop)){
          console.log(prop + ': ' + theObject[prop]);
          if (prop === targetProp) {
            console.log('--found id');
            if (theObject[prop] === targetValue) {
              console.log('----found porop', prop, ', ', theObject[prop]);
              finalResults.push(theObject);
            }
          }
          if (theObject[prop] instanceof Object || theObject[prop] instanceof Array){
            getObject(theObject[prop]);
          }
        }
      }
    }
  }

  getObject(obj);

}

它所做的是找到它的任何对象里面obj有属性名称和值匹配,targetProp并且targetValue,将它推到finalResults阵列。这是可玩的jsfiddle:https ://jsfiddle.net/alexQch/5u6q2ybc/


2
这可以通过以下方式进一步改进return荷兰国际集团的finalResults阵列,而不是接受另一个变量作为输入提供给写入
RozzA


4

使用键和谓词改进@haitaka答案

function  deepSearch (object, key, predicate) {
    if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) return object

    for (let i = 0; i < Object.keys(object).length; i++) {
      let value = object[Object.keys(object)[i]];
      if (typeof value === "object" && value != null) {
        let o = deepSearch(object[Object.keys(object)[i]], key, predicate)
        if (o != null) return o
      }
    }
    return null
}

因此,可以将其调用为:

var result = deepSearch(myObject, 'id', (k, v) => v === 1);

要么

var result = deepSearch(myObject, 'title', (k, v) => v === 'Some Recommends');

这是演示:http : //jsfiddle.net/a21dx6c0/

已编辑

同样,您可以找到多个对象

function deepSearchItems(object, key, predicate) {
        let ret = [];
        if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) {
            ret = [...ret, object];
        }
        if (Object.keys(object).length) {
            for (let i = 0; i < Object.keys(object).length; i++) {
                let value = object[Object.keys(object)[i]];
                if (typeof value === "object" && value != null) {
                    let o = this.deepSearchItems(object[Object.keys(object)[i]], key, predicate);
                    if (o != null && o instanceof Array) {
                        ret = [...ret, ...o];
                    }
                }
            }
        }
        return ret;
    }

2
注意:typeof null === 'object',因此,如果对象中的任何值为null,则它们将在尝试递归时导致函数崩溃。因此,您还应在递归之前检查真实性。
拉菲,

@Rafi感谢您提及它,编辑了答案。
尤利安·平扎鲁

2

@Iulian Pinzaru的答案几乎正是我所需要的,但是如果您的对象具有任何null值,它将无法正常工作。此版本修复了该问题。

function  deepSearch (object, key, predicate) {
  if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) return object

  for (let i = 0; i < Object.keys(object).length; i++) {
    const nextObject = object[Object.keys(object)[i]];
    if (nextObject && typeof nextObject === "object") {
      let o = deepSearch(nextObject, key, predicate)
      if (o != null) return o
    }
  }
  return null
}

2

我们使用对象扫描进行数据处理。从概念上讲,它很简单,但是可以容纳很多很酷的东西。这是您将如何解决您的特定问题的方法

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

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

const data = [{
  title: 'some title',
  channel_id: '123we',
  options: [{
    channel_id: 'abc',
    image: 'http://asdasd.com/all-inclusive-block-img.jpg',
    title: 'All-Inclusive',
    options: [{
      channel_id: 'dsa2',
      title: 'Some Recommends',
      options: [{
        image: 'http://www.asdasd.com',
        title: 'Sandals',
        id: '1',
        content: {}
      }]
    }]
  }]
}];

console.log(find('1', data));
// => { image: 'http://www.asdasd.com', title: 'Sandals', id: '1', content: {} }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.7.1"></script>

免责声明:我是对象扫描的作者


在哪里使用输入?
user3808307 '19

1
@ user3808307已更正
Vincent

1

改进了答案,以考虑对象内的循环引用。它还显示到达目的地所需的路径。

在此示例中,我要搜索一个我知道位于全局对象内某处的iframe:

const objDone = []
var i = 2
function getObject(theObject, k) {
    if (i < 1 || objDone.indexOf(theObject) > -1) return
    objDone.push(theObject)
    var result = null;
    if(theObject instanceof Array) {
        for(var i = 0; i < theObject.length; i++) {
            result = getObject(theObject[i], i);
            if (result) {
                break;
            }   
        }
    }
    else
    {
        for(var prop in theObject) {
            if(prop == 'iframe' && theObject[prop]) {
                i--;
                console.log('iframe', theObject[prop])
                return theObject[prop]
            }
            if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
                result = getObject(theObject[prop], prop);
                if (result) {
                    break;
                }
            } 
        }
    }
    if (result) console.info(k)
    return result;
}

运行以下命令: getObject(reader, 'reader') 给出以下输出和iframe元素:

iframe // (The Dom Element)
_views
views
manager
rendition
book
reader

注意:路径是相反的顺序 reader.book.rendition.manager.views._views.iframe


1

我想提出一个对Zach / RegularMike答案的修正(但没有“声誉”可以发表评论!)。我发现那里的解决方案是一个非常有用的基础,但是在我的应用程序中却遇到了麻烦,因为如果数组中有字符串,它将递归地为字符串中的每个字符调用该函数(这导致IE11和Edge浏览器因“堆栈空间不足”错误而失败)。我的简单优化是将“对象”子句递归调用中使用的相同测试添加到“数组”子句中的测试:

if (arrayElem instanceof Object || arrayElem instanceof Array) {

因此,我的完整代码(现在正在寻找特定键的所有实例,与原始要求略有不同)是:

// Get all instances of specified property deep within supplied object
function getPropsInObject(theObject, targetProp) {
    var result = [];
    if (theObject instanceof Array) {
        for (var i = 0; i < theObject.length; i++) {
            var arrayElem = theObject[i];
            if (arrayElem instanceof Object || arrayElem instanceof Array) {
                result = result.concat(getPropsInObject(arrayElem, targetProp));
            }
        }
    } else {
        for (var prop in theObject) {
            var objProp = theObject[prop];
            if (prop == targetProp) {
                return theObject[prop];
            }
            if (objProp instanceof Object || objProp instanceof Array) {
                result = result.concat(getPropsInObject(objProp, targetProp));
            }
        }
    }
    return result;
}

1

前段时间,我制作了一个小库find-and(可在npm使用),用于以lodash方式处理嵌套对象。有一个returnFound函数可以返回找到的对象,如果有多个对象,则返回一个对象数组。

例如,

const findAnd = require('find-and');

const a = [
  {
    'title': "some title",
    'channel_id':'123we',
    'options': [
      {
        'channel_id':'abc',
        'image':'http://asdasd.com/all-inclusive-block-img.jpg',
        'title':'All-Inclusive',
        'options':[
          {
            'channel_id':'dsa2',
            'title':'Some Recommends',
            'options':[
              {
                'image':'http://www.asdasd.com',
                'title':'Sandals',
                'id':'1',
                'content':{},
              },
            ],
          },
        ],
      },
    ],
  },
];

findAnd.returnFound(a, {id: '1'});

退货

{
  'image':'http://www.asdasd.com',
  'title':'Sandals',
  'id':'1',
  'content':{},
}

1

另一个递归解决方案,适用于数组/列表和对象,或两者的混合:

function deepSearchByKey(object, originalKey, matches = []) {

    if(object != null) {
        if(Array.isArray(object)) {
            for(let arrayItem of object) {
                deepSearchByKey(arrayItem, originalKey, matches);
            }
        } else if(typeof object == 'object') {

            for(let key of Object.keys(object)) {
                if(key == originalKey) {
                    matches.push(object);
                } else {
                    deepSearchByKey(object[key], originalKey, matches);
                }

            }

        }
    }


    return matches;
}

用法:

let result = deepSearchByKey(arrayOrObject, 'key'); // returns an array with the objects containing the key

0

只需使用递归函数。
请参见下面的示例:

const data = [
  {
    title: 'some title',
    channel_id: '123we',
    options: [
      {
        channel_id: 'abc',
        image: 'http://asdasd.com/all-inclusive-block-img.jpg',
        title: 'All-Inclusive',
        options: [
          {
            channel_id: 'dsa2',
            title: 'Some Recommends',
            options: [
              {
                image: 'http://www.asdasd.com',
                title: 'Sandals',
                id: '1',
                content: {},
              }
            ]
          }
        ]
      }
    ]
  }
]

function _find(collection, key, value) {
  for (const o of collection) {
    for (const [k, v] of Object.entries(o)) {
      if (k === key && v === value) {
        return o
      }
      if (Array.isArray(v)) {
        const _o = _find(v, key, value)
        if (_o) {
          return _o
        }
      }
    }
  }
}

console.log(_find(data, 'channel_id', 'dsa2'))


0

    fucntion getPath(obj, path, index = 0) {
        const nestedKeys = path.split('.')
        const selectedKey = nestedKeys[index]

        if (index === nestedKeys.length - 1) {
            return obj[selectedKey]
        }

        if (!obj.hasOwnProperty(selectedKey)) {
            return {}
        }

        const nextObj = obj[selectedKey]

        return Utils.hasPath(nextObj, path, index + 1)
    }

受欢迎的您:Gorillaz


-1

这段代码使您可以获取JSON中其用户定义键的所有对象。

function main(obj = {}, property){
 const views = [];

 function traverse(o) {
    for (var i in o) {
      if(i === property) views.push(o[i]);
      if (!!o[i] && typeof(o[i])=="object") {
        console.log(i, o[i]);
        traverse(o[i]);
      } else {
        console.log(i, o[i]);
      }
    }
    }

  traverse(obj);
  return views;

}



这是一个例子:

const obj = {
    id: 'id at level 1',
    level2: {
      id: 'id at level 2',
      level3: {
        id: 'id at level 3',
        level4: {
          level5: {
            id: 'id at level 5'
          }
       }
    }
  },
  text: ''
}

main(obj, 'id');


-16

如果您已经在使用Underscore,请使用_.find()

_.find(yourList, function (item) {
    return item.id === 1;
});

如果该项目在对象的第一层中,但不在其中,则将其嵌套在里面就可以工作
哈里

2
ang 我在看手机,包裹物体的方式使它看起来像在第一层。我认为这似乎太容易了。
rhodesjason
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.