簡體   English   中英

在嵌套數組中按關鍵字查找

[英]Find by key deep in a nested array

假設我有一個 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':{
                                     ...

我想找到一個 ID 為 1 的 object。是否有這樣的 function ? 我可以使用 Underscore 的_.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/

對我有用的是這種懶惰的方法,而不是算法上的懶惰;)

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

另一個(有點傻)的選擇是利用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; };

我通過谷歌搜索類似的功能找到了這個頁面。 基於 Zach 和 regularmike 提供的工作,我創建了另一個適合我需要的版本。
順便說一句,Zah 和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陣列。 這是要玩的 jsfiddle: https ://jsfiddle.net/alexQch/5u6q2ybc/

我為此目的創建了庫: https : //github.com/dominik791/obj-traverse

您可以像這樣使用findFirst()方法:

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

現在foundObject變量存儲對您正在尋找的對象的引用。

使用鍵和謂詞改進了@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;
    }

我們使用對象掃描進行數據處理。 它在概念上非常簡單,但允許很多很酷的東西。 以下是您解決具體問題的方法

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

免責聲明:我是對象掃描的作者

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

改進的答案以考慮對象內的循環引用。 它還顯示到達那里的路徑。

在此示例中,我正在搜索我知道位於全局對象中某處的 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;
}

前段時間我制作了一個小的 lib 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':{},
}

只需使用遞歸函數。
請參閱下面的示例:

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

您可以在遞歸函數中使用 javascript some函數。 some 的優點是一旦孩子被建立就停止循環。 不要使用在大數據中會變慢的地圖。

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. 檢測深度嵌套對象中的鍵。
  2. 最后返回檢測到的鍵的值。

找到了我正在尋找的答案,尤其是 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');

這將返回包含匹配鍵和值的 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) }

不客氣 作者:Gorillaz

如果你對整個 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!'

如何搜索測試 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: ''
  },
}

這段代碼允許您獲取 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');

如果您已經在使用 Underscore,請使用 _.find()

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM