Javascript найти дочерний объект во вложенных массивах

У меня есть структура javascript, как показано ниже (вложенные массивы объектов)

var categoryGroups = [
    {
        Id: 1, Categories: [
            { Id: 1 },
            { Id: 2 }, 
        ]

    },
    {
        Id: 2, Categories: [
            { Id: 100 },
            { Id: 200 },
        ]

    }
]

Я хочу найти дочерний объект Category, соответствующий идентификатору, при условии, что идентификатор категории уникален.

У меня есть это ниже, но было интересно, есть ли более сжатый способ сделать это:

var category, categoryGroup, found = false;
for (i = 0; i < categoryGroups.length ; i++) {
    categoryGroup = categoryGroups[i];
    for (j = 0; j < categoryGroup.Categories.length; j++) {
        category = categoryGroup.Categories[j];
        if (category.Id === id) {
            found = true;
            break;
        }
    }
    if (found) break;
}

Ответ 1

Предостережение: при этом используется пара функций Array.prototype которые были добавлены только в ECMAScript 5, и, следовательно, не будут работать с более старыми браузерами, если вы не заполните их полностью.

Вы можете перебрать все объекты первого уровня в вашем массиве, а затем отфильтровать категории на основе вашего состояния и собрать все совпадения в массиве. Ваш окончательный результат будет первым элементом в массиве совпадений (совпадение не найдено, если массив пуст).

var matches = [];
var needle = 100; // what to look for

arr.forEach(function(e) {
    matches = matches.concat(e.Categories.filter(function(c) {
        return (c.Id === needle);
    }));
});

console.log(matches[0] || "Not found");

JSFiddle: http://jsfiddle.net/b7ktf/1/

Рекомендации:

Array.prototype.forEach
Array.prototype.concat
Array.prototype.filter

Ответ 2

проверьте код в fiddle

var categoryGroups = [
    {
        Id: 1, Categories: [
            { Id: 1 },
            { Id: 2 }, 
        ]

    },
    {
        Id: 2, Categories: [
            { Id: 100 },
            { Id: 200 },
        ]

    }
]
var id = 100;
var x = 'not found';
var category, categoryGroup, found = false;
for (i = 0; i < categoryGroups.length ; i++) {
    categoryGroup = categoryGroups[i];
    for (j = 0; j < categoryGroup.Categories.length; j++) {
        category = categoryGroup.Categories[j];
        if (category.Id == id) {
            var x = category.Id;
            found = true;
            break;
        }
    }
    if (found) break;
}
alert(x);

Вышеприведенный код проверяет, найден ли в массиве id = 100. Если найденный будет предупреждать, что значение else предупреждает, что его не найдено. значение "100" было жестко запрограммировано для демонстрации

Ответ 3

Вы можете обернуть его внутри функции, чтобы избавиться от неудобного синтаксиса break;, и вы можете загрузить каждый элемент в переменную внутри конструкции for(;;), чтобы сбрить несколько строк.

function subCategoryExists(groups, id)
{
  for (var i = 0, group; group = groups[i]; ++i) {
    for (var k = 0, category; category = group.Categories[k]; ++k) {
      if (category.Id == id) {
        return true;
      }
    }
  }
  return false;
}

var found = subCategoryExists(categoryGroups, 100);

Ответ 4

Простой способ использования lodash-библиотеки NodeJS (при условии, что вы используете NodeJS):

const _ = require('lodash');
let category ;
let categoryGroup = _.find(categoryGroups, (element)=>{
  category = _.find(element.Categories, {Id : 100});
  return category;
});

console.log(categoryGroup); // The category group which has the sub category you are looking for
console.log(category); // The exact category you are looking for

Ответ 5

Использование уменьшения и рекурсии:

function nestedSearch(value) {
    return categoryGroups.reduce(function f(acc, val) {
        return (val.Id === value) ? val :
            (val.Categories && val.Categories.length) ? val.Categories.reduce(f, acc) : acc;
    });
}

> попробуйте на JSFiddle

Ответ 6

Использование только Array.prototype.filter():

Если вы уверены, что искомый идентификатор существует, вы можете сделать следующее:

var id = 200; // surely it exists
var category = arr.filter(g => g.Categories.filter(c => c.Id === id)[0])[0].Categories.filter(c => c.Id === id)[0];

Если вы не уверены, что он существует:

var id = 201; // maybe it does not exist
var categoryGroup = arr.filter(e => e.Categories.filter(c => c.Id === id)[0])[0]; 
var category = categoryGroup ? categoryGroup.Categories.filter(c => c.Id === id)[0] : null;

jsfiddle

Ответ 7

Вы можете использовать underscore:

var cat = _(categoryGroups).
  chain().
  pluck('Categories').
  flatten().
  findWhere({Id: 2}).
  value();

Что я делаю здесь, так это то, что я извлекаю все значения Categories в одном массиве, а затем grepping для правильных категорий.

РЕДАКТИРОВАТЬ: извините, я не получил ваш вопрос в первый раз. Как следует из комментариев, вы можете не захотеть использовать подчеркивание только для этого, но так, как я это сделаю:)