Javascript рекурсивно преобразует объект, который ссылается на другой атрибут, используя Lodash или другую библиотеку

#javascript #arrays #object

#javascript #массивы #объект

Вопрос:

Я хочу преобразовать объект javascript, которые ссылаются друг на друга по его атрибуту,

Говорит, что у меня есть этот объект

 {
  apple: {
    banana: [1,2,3],
    cherry: [4,5,6],
  },
  banana: {
    date: [7],
    cherry: [8,9],
    elderberry: [10, 11],
  },
  cherry: {
   date: [7],
   fig:  [12,13],
  },
  date: {
    fig: [11,14],
  },
},
  

И я хочу преобразовать этот объект в этот

 {
  apple: {
    banana: [1,2,3],
    cherry: [4,5,6, 8,9],
    date: [7],
    elderberry: [10, 11],
    fig: [11,14, 12,13],
  },
  banana: {
    cherry: [8,9],
    elderberry: [10, 11],
    fig: [11,14, 12,13],
    date: [7],
  },
  cherry: {
    date: [7],
    fig: [11,14, 12,13],
  },
  date: {
    fig: [11,14],
  },
}
  

В этом примере атрибут cherry в apple имеет [4,5,6, 8,9],
[4,5,6] исходит от apple, а [8, 9] исходит от banana, потому что apple ссылается на banana, а banana имеет ссылку на вишню, поэтому он будет объединен в [4,5,6, 8,9]

а также конечный массив на самом деле является уникальным значением

Таким образом, идея заключается в том, что он будет рекурсивно объединять другое значение компонента, использование lodash или другой библиотеки в порядке ~

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

1. Вы говорите рекурсивно, но похоже, что ваш пример просматривается только до определенной глубины. Например: from a , to b , to d , мы получаем to f: [11, 14] . Так почему этого нет в f списке a ?

2. извините, обновлю пример

3. Что происходит, когда в конечных массивах есть повторяющиеся значения? Не могли бы вы описать это также в своем примере?

4. на самом деле конечный массив всегда уникален, уже обновите пример, спасибо 🙂

5. Примеры, в которых для имен свойств используются одиночные буквы, значительно затрудняют обсуждение проблемы («C, являющийся свойством a, или c, являющийся свойством b»). Гораздо лучше использовать уникальные слова.

Ответ №1:

Сначала я бы создал структуру, которая хранит противоположные отношения, то есть от дочернего элемента к родительскому.

Затем для каждой пары ключ / массив следуйте по пути (через новую структуру) вверх к его предкам и для каждого из них добавьте массив к одному и тому же ключу. Для этого я выбрал обход с использованием явной переменной стека, но он будет работать так же хорошо с рекурсивным обходом DFS.

Наконец, снова посетите все массивы, чтобы удалить дубликаты.

 function complete(data) {
    // Create child-parent relationships:
    const parents = {};
    for (const parent in data) {
        for (const child in data[parent]) {
            (parents[child] = parents[child] || []).push(parent);
        }
    }
    // Tree traveral to copy child array into same key in ancestors
    const result = {};
    for (const parent in data) {
        for (const child in data[parent]) {
            const arr = data[parent][child];
            const visited = new Set;
            const stack = [parent];
            while (stack.length) {
                const node = stack.pop();
                if (visited.has(node)) continue;
                visited.add(node);
                ((result[node] = result[node] || {})[child] = result[node][child] || []).push(...arr);
                stack.push(...parents[node] || []);
            }
        }
    }
    // Remove duplicate values from the arrays
    for (const parent in result) {
        for (const child in result[parent]) {
            result[parent][child] = [...new Set(result[parent][child])];
        }
    }
    return resu<
}

// Example call with data from the question:
const data = {apple: {banana: [1,2,3],cherry: [4,5,6],},banana: {date: [7],cherry: [8,9],elderberry: [10, 11],},cherry: {date: [7],fig:  [12,13],},date: {fig: [11,14],},};
const result = complete(data);
console.log(result);  

Ответ №2:

Вы можете использовать рекурсивный подход для каждого вложенного ключа ( addKeys функции), используя Object.entries() with Array.reduce() и передавая текущие существующие ключи при каждом повторном вызове функции, чтобы избежать вечного цикла.

Затем вы можете уменьшить записи data объекта и вызвать addKeys для каждого ключа.

 const data = {"apple":{"banana":[1,2,3],"cherry":[4,5,6]},"banana":{"date":[7],"cherry":[8,9],"elderberry":[10,11]},"cherry":{"date":[7],"fig":[12,13]},"date":{"fig":[11,14]}}

const addKeys = (obj = {}, existing = []) => 
  Object.entries(obj)
    .reduce((r, [k, v]) =>
      existing.includes(k) ? r : 
      ({
        ...r,
        [k]: v,
        ...addKeys(data[k], [...existing, k, ...Object.keys(r)]),
      })
    , {})

// iterate the data object and rebuild it by adding keys to each sub-key
const result = Object.entries(data)
  .reduce((r, [k, v]) => {
    r[k] = addKeys(v);
    
    return r;
  }, {})
  
console.log(result)  

Ответ №3:

Вы могли бы использовать вложенный подход с рекурсивной функцией с маркером выхода.

 const
    iter = (object, key, target, sub) => Object
        .keys(object[key] || {})
        .reduce((o, k) => {
            o[k] = Array.from(new Set([...(o[k] || []), ...object[key][k]]));
            return sub
                ? iter(object, k, o)
                : o;
        }, target);

var object = { apple: { banana: [1, 2, 3], cherry: [4, 5, 6], }, banana: { date: [7], cherry: [8, 9], elderberry: [10, 11], }, cherry: { date: [7], fig: [12, 13], }, date: { fig: [11, 14] } },
    result = Object.keys(object).reduce((o, k) => {
        iter(object, k, o[k] = o[k] || {}, true);
        return o;
    }, {});

console.log(result);  
 .as-console-wrapper { max-height: 100% !important; top: 0; }