简体   繁体   English

在嵌套数组中按关键字查找

[英]Find by key deep in a nested array

Let's say I have an object:假设我有一个 object:

[
    {
        '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':{
                                     ...

I want to find the one object where the id is 1. Is there a function for something like this?我想找到一个 ID 为 1 的 object。是否有这样的 function ? I could use Underscore's _.filter method, but I would have to start at the top and filter down.我可以使用 Underscore 的_.filter方法,但我必须从顶部开始向下过滤。

Recursion is your friend.递归是你的朋友。 I updated the function to account for property arrays:我更新了函数以说明属性数组:

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

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

What worked for me was this lazy approach, not algorithmically lazy ;)对我有用的是这种懒惰的方法,而不是算法上的懒惰;)

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

If you want to get the first element whose id is 1 while object is being searched, you can use this function:如果要在搜索对象时获取第一个 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;
}

If you want to get all elements whose id is 1, then (all elements whose id is 1 are stored in result as you see):如果要获取所有 id 为 1 的元素,则(如您所见,所有 id 为 1 的元素都存储在 result 中):

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

Another (somewhat silly) option is to exploit the naturally recursive nature of JSON.stringify , and pass it a replacer function which runs on each nested object during the stringification process:另一个(有点傻)的选择是利用JSON.stringify的自然递归特性,并在字符串化过程中向它传递一个在每个嵌套对象上运行的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(entireObj, (_, nestedValue) => { if (nestedValue && nestedValue[keyToFind] === valToFind) { foundObj = nestedValue; } return nestedValue; }); return foundObj; };

I found this page through googling for the similar functionalities.我通过谷歌搜索类似的功能找到了这个页面。 Based on the work provided by Zach and regularmike, I created another version which suits my needs.基于 Zach 和 regularmike 提供的工作,我创建了另一个适合我需要的版本。
BTW, teriffic work Zah and regularmike!顺便说一句,Zah 和regularmike 干得好! I'll post the code here:我会在这里发布代码:

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

}

What it does is it find any object inside of obj with property name and value matching to targetProp and targetValue and will push it to the finalResults array.它所做的是找到它的任何对象内部obj与属性名称和值匹配targetProptargetValue ,并将它推到finalResults阵列。 And Here's the jsfiddle to play around: https://jsfiddle.net/alexQch/5u6q2ybc/这是要玩的 jsfiddle: https ://jsfiddle.net/alexQch/5u6q2ybc/

I've created library for this purpose: https://github.com/dominik791/obj-traverse我为此目的创建了库: https : //github.com/dominik791/obj-traverse

You can use findFirst() method like this:您可以像这样使用findFirst()方法:

var foundObject = findFirst(rootObject, 'options', { 'id': '1' });

And now foundObject variable stores a reference to the object that you're looking for.现在foundObject变量存储对您正在寻找的对象的引用。

Improved @haitaka answer, using the key and predicate使用键和谓词改进了@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
}

So this can be invoked as:所以这可以被调用为:

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

or要么

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

Here is the demo: http://jsfiddle.net/a21dx6c0/这是演示: http : //jsfiddle.net/a21dx6c0/

EDITED已编辑

In the same way you can find more than one object以同样的方式,您可以找到多个对象

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

We use object-scan for our data processing.我们使用对象扫描进行数据处理。 It's conceptually very simple, but allows for a lot of cool stuff.它在概念上非常简单,但允许很多很酷的东西。 Here is how you would solve your specific question以下是您解决具体问题的方法

 // 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.8.0"></script>

Disclaimer : I'm the author of object-scan免责声明:我是对象扫描的作者

@Iulian Pinzaru's answer was almost exactly what I needed, but it doesn't work if your objects have any null values. @Iulian Pinzaru 的答案几乎正是我所需要的,但如果您的对象具有任何空值,则它不起作用。 This version fixes that.这个版本修复了这个问题。

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
}

Another recursive solution, that works for arrays/lists and objects, or a mixture of both:另一个递归解决方案,适用于数组/列表和对象,或两者的混合:

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

usage:用法:

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

Improved answer to take into account circular references within objects.改进的答案以考虑对象内的循环引用。 It also displays the path it took to get there.它还显示到达那里的路径。

In this example, I am searching for an iframe that I know is somewhere within a global object:在此示例中,我正在搜索我知道位于全局对象中某处的 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;
}

Running the following: getObject(reader, 'reader') gave the following output and the iframe element in the end:运行以下命令: getObject(reader, 'reader')给出了以下输出和 iframe 元素:

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

NOTE: The path is in reverse order reader.book.rendition.manager.views._views.iframe注意:路径是逆序的reader.book.rendition.manager.views._views.iframe

I'd like to suggest an amendment to Zach/RegularMike's answer (but don't have the "reputation" to be able to comment!).我想建议对 Zach/RegularMike 的回答进行修正(但没有“声誉”可以发表评论!)。 I found there solution a very useful basis, but suffered in my application because if there are strings within arrays it would recursively call the function for every character in the string (which caused IE11 & Edge browsers to fail with "out of stack space" errors).我发现那里的解决方案是一个非常有用的基础,但在我的应用程序中受到影响,因为如果数组中有字符串,它会为字符串中的每个字符递归调用该函数(这导致 IE11 和 Edge 浏览器因“堆栈空间不足”错误而失败)。 My simple optimization was to add the same test used in the "object" clause recursive call to the one in the "array" clause:我的简单优化是将“object”子句递归调用中使用的相同测试添加到“array”子句中的测试:

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

Thus my full code (which is now looking for all instances of a particular key, so slightly different to the original requirement) is:因此,我的完整代码(现在正在查找特定键的所有实例,与原始要求略有不同)是:

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

Some time ago I have made a small lib find-and , which is available on npm , for working with nested objects in a lodash manner.前段时间我制作了一个小的 lib find-and ,它在npm上可用,用于以 lodash 方式处理嵌套对象。 There's the returnFound function which returns the found object, or an object array if there's more than one object found.returnFound函数返回找到的对象,如果找到多个对象,则返回一个对象数组。

Eg,例如,

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

returns返回

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

Just use recursive function.只需使用递归函数。
See example below:请参阅下面的示例:

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

You can use javascript some function inside a recursive function.您可以在递归函数中使用 javascript some函数。 The advantage of some is to stop looping once the child is founded. some 的优点是一旦孩子被建立就停止循环。 Do not use map that would be slow in large data.不要使用在大数据中会变慢的地图。

const findChild = (array, id) => {
  let result;
  array.some(
    (child) =>
      (child.id === id && (result = child)) ||
      (result = findChild(child.options || [], id))
  );
  return result;
};

findNode(array, 1)

 function getPropFromObj(obj, prop) { let valueToFindByKey; if (!Array.isArray(obj) && obj !== null && typeof obj === "object") { if (obj.hasOwnProperty(prop)) { valueToFindByKey = obj[prop]; console.log(valueToFindByKey); } else { let i; for (i = 0; i < Object.keys(obj).length; i++) { getPropFromObj(obj[Object.keys(obj)[i]], prop); } } } return null; } const objToInvestigate = { employeeInformation: { employees: { name: "surya", age: 27, job: "Frontend Developer", }, }, }; getPropFromObj(objToInvestigate, "name");

  1. Detecting the key in the deeply nested object.检测深度嵌套对象中的键。
  2. Finally return the value of the detected key.最后返回检测到的键的值。

Found the answer I was looking for, especially Ali Alnoaimi's solution.找到了我正在寻找的答案,尤其是 Ali Alnoaimi 的解决方案。 I made some small adjustments to allow for the search of the value as well我做了一些小的调整以允许搜索值

function deepSearchByKey(object, originalKey, originalValue, matches = []) {
if (object != null) {
  if (Array.isArray(object)) {
    for (let arrayItem of object) {
      deepSearchByKey(arrayItem, originalKey, originalValue, matches);
    }
  } else if (typeof object == 'object') {
    for (let key of Object.keys(object)) {
      if (key == originalKey) {
        if (object[key] == originalValue) {
          matches.push(object);
        }
      } else {
        deepSearchByKey(object[key], originalKey, originalValue, matches);
      }
    }
  }
}

return matches;
}

To use:要使用:

let result = deepSearchByKey(arrayOrObject, 'key', 'value');

This will return the object containing the matching key and value.这将返回包含匹配键和值的 object。

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

You're welcome By: Gorillaz不客气 作者:Gorillaz

If you're into the whole ES6 thing you can use如果你对整个 ES6 感兴趣,你可以使用

const findByKey = (obj, kee) => {
    if (kee in obj) return obj[kee];
    for(n of Object.values(obj).filter(Boolean).filter(v => typeof v === 'object')) {
        let found = findByKey(n, kee)
        if (found) return found
    }
}

const arry = [{ foo: 0 }, null, { bar: [{ baz: { nutherKey: undefined, needle: "gotcha!" } }]}]
const obj = { alice: Infinity, bob: NaN, charlie: "string", david: true, ebert: arry }

findByKey(obj, 'needle')
// 'gotcha!'

How to search for Testing 5 or Testing 3 nested values and if they exist return 'name' value.如何搜索测试 5 或测试 3 嵌套值以及它们是否存在返回“名称”值。

{
  name: 'Test1',
  info: {
    Test 1: 'Test 1',
    'Test 3': 'Testing 3',
    'Test 4': 'Testing 4',
    Test 5: 'Testing 5'
  },
  name: 'Test2',
  info: {
    Test 1: 'Test 1',
    'Test 3': 'Testing 3',
    'Test 4': 'Testing 4',
    Test 5: ''
  },
}

This piece of code allows you to get all objects within a JSON whose key is user-defined.这段代码允许您获取 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;

}



Here is an example:下面是一个例子:

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

If you're already using Underscore, use _.find()如果您已经在使用 Underscore,请使用 _.find()

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM