Как рекурсивно развернуть глубоко вложенные объекты? - PullRequest
1 голос
/ 07 февраля 2020

Я работаю с массивом, который может иметь количество объектов / массивов непредсказуемое вложение. Каждый из объектов всегда будет иметь свойство name, а некоторые из этих объектов могут иметь массив sub_fields. Я sh выделю объект так, чтобы ключ каждого объекта равнялся свойству name, сохраняя при этом уровни вложенности от оригинала.

Вот пример того, с чего я мог бы начать:

var data = [
    {
        foo: 'foo',
        bar: 'bar',
        name: 'Object 1'
    },
    {
        foo: 'foo',
        bar: 'bar',
        name: 'Object 2'
    },
    {
        foo: 'foo',
        bar: 'bar',
        name: 'Object 3',
        sub_fields : [
            {
                foo: 'foo',
                bar: 'bar',
                name: 'SubLevel Object 1',
                sub_fields: [
                    {
                        foo: 'foo',
                        bar: 'bar',
                        name: 'SubLevel Object 1',
                    }
                ]
            },
            {
                foo: 'foo',
                bar: 'bar',
                name: 'SubLevel Object 2',
                sub_fields: [
                    {
                        foo: 'foo',
                        bar: 'bar',
                        name: 'SubLevel Object 1',
                    },
                    {
                        foo: 'foo',
                        bar: 'bar',
                        name: 'SubLevel Object 2',
                    },
                    {
                        foo: 'foo',
                        bar: 'bar',
                        name: 'SubLevel Object 3',
                        sub_fields: [
                            {
                                foo: 'foo',
                                bar: 'bar',
                                name: 'SubLevel Object 1'
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

В идеале я собираюсь взять эти исходные данные и создать что-то вроде этого:

var newData = {
    'Object 1': null,
    'Object 2': null,
    'Object 3': {
        'Sublevel Object 1': {
            'Sublevel Object 1': null
        },
        'Sublevel Object 2': {
            'Sublevel Object 1': null,
            'Sublevel Object 2': null,
            'Sublevel Object 3': {
                  'Sublevel Object 1': null
             }
        }
    }
}

Мне кажется, что я трачу слишком много времени на это, когда знаю, что должна быть какая-то рекурсивная функция, чтобы это произошло - я просто слишком неопытен с тем, что я Я изо всех сил пытаюсь найти решение.

Может кто-нибудь показать мне, что я могу сделать?

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

(также, анализируемая "field_group" взята из более релевантных JSON данных, с которыми я работаю ... а не из приведенного выше примера "foo / bar".)

var parsed_field_group = JSON.parse(JSON.stringify(field_group));
var fields = field_group[0].fields;

function removeKeys(obj, keys) {
    for (var prop in obj) {
        if(obj.hasOwnProperty(prop)) {
            switch(typeof(obj[prop])) {
                case 'object':
                    if(keys.indexOf(prop) > -1) {
                        delete obj[prop];
                    } else {
                        removeKeys(obj[prop], keys);
                    }
                    break;
                default:
                    if(!keys.includes(prop)) {
                        delete obj[prop];
                    }
                    break;
            }
        }
    }
}

removeKeys(fields, ['name', 'wrapper'])

console.log(fields)

var newJson = Object.create(null);
fields.forEach(field => {
    if (field.hasOwnProperty('sub_fields')) {
        newJson[field.name] = field
    } else {
        newJson[field.name] = null
    }
})

function handleSubFields(obj, key) {
    for (var prop in obj) {
        if (obj[prop]) {
            obj[prop][key].forEach(field => {
                if (field.hasOwnProperty(key)) {
                    obj[prop][field.name] = field
                } else {
                    obj[prop][field.name] = null
                }
            })
            delete obj[prop][key]
            delete obj[prop].name
        }
    }
}
handleSubFields(newJson, 'sub_fields')

console.log(newJson)

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

Я знаю, что вышеперечисленное должно быть смешным. Пожалуйста, будьте добры! :-)

Ответы [ 4 ]

2 голосов
/ 07 февраля 2020

Для преобразования

{ foo: 'foo'
, bar: 'bar'
, name: 'Object 1'
}

в:

{ 'Object 1': null
}

Вы можете использовать эту функцию:

const transform =
  ({name}) =>
    ({[name]: null});

Что, если она содержит sub_fields ?

Вместо присвоения null присвойте результат применения transform ко всем объектам в sub_fields.

. Мы можем изменить transform для поддержки этого:

const transform =
  ({name, sub_fields}) =>
    ( { [name]: sub_fields
          ? Object.assign({}, ...sub_fields.map(transform))
          : null
      }
    );

Теперь вы можете применить тот же принцип к нашему массиву для создания вашего конечного объекта:

const transform =
  ({name, sub_fields}) =>
    ( { [name]: sub_fields
          ? Object.assign({}, ...sub_fields.map(transform))
          : null
      }
    );
    
    
console.log(

  Object.assign({}, ...data.map(transform))

);
<script>
const data = [{"foo":"foo","bar":"bar","name":"Object 1"},{"foo":"foo","bar":"bar","name":"Object 2"},{"foo":"foo","bar":"bar","name":"Object 3","sub_fields":[{"foo":"foo","bar":"bar","name":"SubLevel Object 1","sub_fields":[{"foo":"foo","bar":"bar","name":"SubLevel Object 1"}]},{"foo":"foo","bar":"bar","name":"SubLevel Object 2","sub_fields":[{"foo":"foo","bar":"bar","name":"SubLevel Object 1"},{"foo":"foo","bar":"bar","name":"SubLevel Object 2"},{"foo":"foo","bar":"bar","name":"SubLevel Object 3","sub_fields":[{"foo":"foo","bar":"bar","name":"SubLevel Object 1"}]}]}]}];
</script>
1 голос
/ 09 февраля 2020

Уже есть несколько хороших ответов и хорошая экспозиция от customcommander. Я думаю, что эта альтернатива заслуживает рассмотрения по нескольким причинам:

  • Я выбираю reduce, только если что-то более явное недоступно. Хотя это самый мощный из методов итерации массива, он более низкого уровня, и для меня он не так самодокументирован, как эта комбинация map и Object.assign. (Это еще более важно по сравнению с forEach, который вообще не имеет внутренней семантики.)

  • Я лично предпочитаю работать с выражениями, а не с операторами, и предпочитаю стрелка с одним выражением для одного с одним или несколькими операторами return.

  • Несмотря на то, что экспозиция customcommander великолепна, а техника мне очень нравится, функция transform все еще нуждается в обертка, чтобы получить ожидаемый результат. В оболочках нет абсолютно ничего плохого (хотя я бы добавил для этого еще одну функцию). Но предлагаемая здесь версия управляет тем же с помощью аналогичных методов и без дополнительной сложности без использования оболочки.

const transform = (obj) =>
  Array .isArray (obj)
    ? Object .assign (...obj .map (transform))
    : {[obj .name] : 'sub_fields' in obj ? transform (obj .sub_fields) : null}

const data = [{ foo: 'foo', bar: 'bar', name: 'Object 1' }, { foo: 'foo', bar: 'bar', name: 'Object 2' }, { foo: 'foo', bar: 'bar', name: 'Object 3', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1', }] }, { foo: 'foo', bar: 'bar', name: 'SubLevel Object 2', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1', }, { foo: 'foo', bar: 'bar', name: 'SubLevel Object 2', }, { foo: 'foo', bar: 'bar', name: 'SubLevel Object 3', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1' }] } ] } ] } ]

console .log (transform (data))

Я не хочу утверждать, что с другими решениями что-то не так, только то, что эта комбинация факторов означала, что есть место для еще одной техники.

0 голосов
/ 07 февраля 2020

Я разрабатываю эту функцию, используя рекурсию для достижения вашей цели, зная, что в вашем объекте всегда будет атрибут с именем 'name', а иногда и другой, называемый 'sub_fields'.

function rebuildNameObject(objArr = [], resObj = {}){
  objArr.forEach(obj => {
        Object.keys(obj).forEach(item => {
          if (item === 'name'){
            resObj[obj[item]] = null;
          }

          if(item === 'sub_fields'){
            resObj[obj['name']] = rebuildNameObject(obj[item]);
          }

        });
    });
  return resObj;
}
0 голосов
/ 07 февраля 2020

Да, это очень легко с помощью рекурсии. Вот ваш основной случай c:

  • Вы получаете что-то, что не является объектом - вы возвращаете null.

Тогда ваш базовый c случай рекурсии:

  • Вы получаете объект, вы создаете новый объект со свойством name объекта, значением являясь рекурсивным приложением функции.

Наконец, вам просто нужно разобраться с тем, что происходит, если у вас есть массивы:

  • Обойти весь массив и вызвать функцию для каждого элемент. Соберите все результаты в один и тот же объект.

var data = [{ foo: 'foo', bar: 'bar', name: 'Object 1' }, { foo: 'foo', bar: 'bar', name: 'Object 2' }, { foo: 'foo', bar: 'bar', name: 'Object 3', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1', }] }, { foo: 'foo', bar: 'bar', name: 'SubLevel Object 2', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1', }, { foo: 'foo', bar: 'bar', name: 'SubLevel Object 2', }, { foo: 'foo', bar: 'bar', name: 'SubLevel Object 3', sub_fields: [{ foo: 'foo', bar: 'bar', name: 'SubLevel Object 1' }] } ] } ] } ]


function toObj(data) {
  if (data == null) return null;
  
  //collect all recursive calls to the function in the same object
  if (Array.isArray(data))
    return data.reduce((acc, data) => ({ ...acc, ...toObj(data)}), {})
  
  //produce a new object from the `name` property and a recursive application of the function
  return { [data.name]: toObj(data.sub_fields, {}) };
}

console.log(toObj(data))
...