Поиск по ключевому слову во вложенном объекте
Скажем, у меня есть объект:
[
{
'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
, но мне нужно было бы начать сверху и отфильтровать.
Ответы
Ответ 1
Рекурсия - ваш друг. Я обновил функцию для учета массивов свойств:
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/
Ответ 2
Если вы хотите получить первый элемент с идентификатором 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;
}
Если вы хотите получить все элементы с идентификатором 1, то (все элементы с идентификатором 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);
}
}
}
Ответ 3
Что сработало для меня, так это ленивый подход, а не алгоритмически ленивый
if( JSON.stringify(object_name).indexOf("key_name") > -1 ) {
console.log("Key Found");
}
else{
console.log("Key not Found");
}
Ответ 4
Я нашел эту страницу через поиск в Google для подобных функций. Основываясь на работе, предоставленной Заком и регуляром, я создал еще одну версию, которая соответствует моим потребностям.
Кстати, потрясающая работа, Зах и регулярка! Я выложу код здесь:
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/
Ответ 5
Я создал библиотеку для этой цели: https://github.com/dominik791/obj-traverse
Вы можете использовать метод findFirst()
следующим образом:
var foundObject = findFirst(rootObject, 'options', { 'id': '1' });
И теперь переменная foundObject
хранит ссылку на объект, который вы ищете.
Ответ 6
Улучшен ответ @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++) {
if (typeof object[Object.keys(object)[i]] === "object") {
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');
Вот jsFiddle: http://jsfiddle.net/ktdx9es7
Ответ 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;
};
Ответ 8
Улучшенный ответ для учета циклических ссылок внутри объектов. Он также отображает путь, который понадобился, чтобы туда добраться.
В этом примере я ищу 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
Ответ 9
Я хотел бы предложить поправку к ответу 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;
}
Ответ 10
Я бы постарался не изобретать велосипед. Мы используем object-scan для всех наших потребностей в обработке данных. Это концептуально очень просто, но допускает много интересных вещей. Вот как бы вы решили свой конкретный вопрос
Определение данных
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': {}
}]
}]
}]
}];
Logic
const objectScan = require('object-scan');
const scanner = (input) => {
let obj = null;
objectScan(['**.id'], {
filterFn: (key, value, { parents }) => {
if (value === '1') {
obj = parents[0];
}
},
breakFn: () => obj !== null
})(data);
return obj;
};
const result = scanner(data);
Выход
// result =>
{
"image": "http://www.asdasd.com",
"title": "Sandals",
"id": "1",
"content": {}
}
Ответ 11
Если вы уже используете Underscore, используйте _.find()
_.find(yourList, function (item) {
return item.id === 1;
});