在嵌套数组中按键深度查找



假设我有一个对象:

[
    {
        '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的对象。有这样的函数吗?我可以使用下划线的_.filter方法,但我必须从顶部开始,然后向下过滤。

递归是你的朋友。我更新了函数,以考虑属性数组:

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/

另一个(有点傻的)选择是利用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;
};

适合我的是这种懒惰的方法,而不是算法上的懒惰;)

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

如果您想在对象被搜索时获得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的所有元素都存储在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);
        }
    }
}

改进@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;
    }

如果你对整个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 findByProperty = (obj, predicate) => {
    if (predicate(obj)) return obj
    for(n of Object.values(obj).filter(Boolean).filter(v => typeof v === 'object')) {
        let found = findByProperty(n, predicate)
        if (found) return found
    }
}

find by value会有点不同

let findByValue = (o, val) => {
    if (o === val) return o;
    if (o === NaN || o === Infinity || !o || typeof o !== 'object') return;
    if (Object.values(o).includes(val)) return o;
    for (n of Object.values(o)) {
        const found = findByValue(n, val)
        if (found) return n
    }
}

则可以这样使用

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!'
findByProperty(obj, val => val.needle === 'gotcha!')
// { nutherKey: undefined, needle: "gotcha!" }
findByValue(obj, 'gotcha!')
// { nutherKey: undefined, needle: "gotcha!" }

我通过谷歌搜索类似的功能找到了这个页面。根据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内部的任何对象,其属性名称和值与targetProptargetValue匹配,并将其推入finalResults数组。这里有一个例子:https://jsfiddle.net/alexQch/5u6q2ybc/

我为此创建了一个库:https://github.com/dominik791/obj-traverse

你可以这样使用findFirst()方法:

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

现在foundObject变量存储了对您正在查找的对象的引用。

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

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

找到了我一直在寻找的答案,尤其是Ali Alnoaimi的解决方案。我做了一些小的调整,以允许搜索值

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;
}
使用:

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

这将返回包含匹配键和值的对象。

您可以在递归函数中使用javascript some函数。一些方法的优点是一旦孩子被建立就停止循环。在大数据中不要使用速度很慢的map。

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

直接使用递归函数。
请看下面的例子:

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

我们使用对象扫描进行数据处理。它在概念上非常简单,但允许很多很酷的东西。以下是你如何解决你的具体问题

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

免责声明:我是object-scan的作者

@Iulian Pinzaru的答案几乎正是我需要的,但如果你的对象有任何空值,它就不起作用了。这个版本修复了这个问题。

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
}

          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. 检测深度嵌套对象中的键。
  2. 最后返回检测到的键值。

考虑对象内循环引用的改进答案。它还显示了到达那里的路径。

在这个例子中,我正在搜索一个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

我想对Zach/RegularMike的答案提出一个修改建议(但我没有足够的"声誉"来评论!)我发现这个解决方案是一个非常有用的基础,但在我的应用程序中受到了影响,因为如果数组中有字符串,它会递归地为字符串中的每个字符调用函数(这导致IE11 &Edge浏览器会出现"堆栈空间不足"错误)。我的简单优化是将"object"子句递归调用中使用的相同测试添加到"array"子句中的测试:

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

前段时间,我做了一个小库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':{},
}

此函数(main())允许您获取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") traverse(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: ''
}
console.log(main(obj, 'id'));


更容易和更干净的方法

const findElement = (searchObj, searchKey) => Object.keys(searchObj).forEach(key => {
  if (key === searchKey) {
    preloadingImgObj = searchObj[key];
    return searchObj[key];
  }
  if (typeof searchObj[key] === 'object' && searchObj[key] !== undefined && searchObj[key] !== null) {
    return findElement(searchObj[key], searchKey);
  }
});

Thank me later😜

如果您已经使用下划线,请使用_.find()

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

最新更新