Отфильтруйте объект по значению ключа, чтобы получить ключи всех затронутых объектов

#javascript #ecmascript-6

Вопрос:

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

На первом шаге я фильтрую два из пяти элементов: one и two ; и ожидаемый результат [«@область/один», «@область/два»].

  • Но @scope/one есть deps ключ, поэтому мне нужно добавить three и two еще к результату. @scope/two уже существует в результирующем массиве.
  • И @scope/three имеет four as deps, который теперь также должен быть добавлен к результату.
  • @scope/four не имеет депов, поэтому цикл завершен.

В этом примере результат должен быть ['@scope/one', '@scope/two', '@scope/three', '@scope/four'] .

Другими словами: мне нужно отфильтровать некоторые элементы, а затем выполнить цикл для каждого deps и добавить этот ключ также в результирующий массив.

 const data = {
  '@scope/one': { deps: { three: { foo: 'bar' }, two: { foo: 'bar' } } },
  '@scope/two': { foo: 'bar' },
  '@scope/three': { deps: { four: { foo: 'bar' } } },
  '@scope/four': { foo: 'bar' },
  '@scope/five': { foo: 'bar' },
};

function sanitize(data, whitelist) {
  return whitelist.reduce(
    (result, key) => (data[key] !== undefined ? Object.assign(result, { [key]: data[key] }) : result),
    {}
  );
}

const filtered = sanitize(data, ['one', 'two']);
const result = Object.keys(filtered);

console.log(result); 

Ответ №1:

Поддерживайте очередь ключей и продолжайте нажимать deps , пока очередь не опустеет:

 function pick(obj, keys) {
    let res = new Set(), queue = [...keys]

    while (queue.length) {
        let key = '@scope/'   queue.shift()

        if (!res.has(key)) {
            res.add(key)

            let val = obj[key]
            if (val.deps)
                queue.push(...Object.keys(val.deps))
        }
    }

    return [...res]
}

//

const data = {
    '@scope/one': {deps: {three: {foo: 'bar'}, two: {foo: 'bar'}}},
    '@scope/two': {foo: 'bar'},
    '@scope/three': {deps: {four: {foo: 'bar'}}},
    '@scope/four': {deps: {one: {foo: 'bar'}}}, // cycle!
    '@scope/five': {foo: 'bar'},
};

console.log(pick(data, ['one', 'two'])) 

Если возможны циклические зависимости, добавьте if(!res.has(key)) проверку, чтобы разорвать их.

В качестве примечания, это выглядело бы более прозрачно, если deps бы были просто списки ключей, а не полные объекты.

Комментарии:

1. Спасибо. Я упустил важную вещь: ключи «первого уровня» имеют «область действия» в своем названии, у deps этого нет. В результате мне всегда нужны ключи «первого уровня» с областью действия. Я обновил свой пост. let val = obj['@scope/' key] кажется, это не то место… Извините, что пропустил область в первоначальном посте.

2. без проблем, одно незначительное изменение: let key = '@scope/' queue.shift()

3. Я добавил if(!res.has(key)) res.add(key) , но циклы по-прежнему бесконечны

4. У меня все еще есть бесконечный цикл с моими данными. Я думаю, проблема в том, что мы добавляем в очередь сразу несколько элементов и одновременно удаляем только один элемент. Таким образом, даже если мы проверим наличие существующих элементов в res, может быть несколько циклов, так как для каждого элемента может быть несколько dep.