обновить вложенный объект json с помощью рекурсии в JavaScript - PullRequest
3 голосов
/ 01 октября 2019

Я пытаюсь создать обновленный объект из существующего объекта. Пример объекта:

// sample object
const testObj = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3,
      f: {
        g: 4
      }
    }
  }
};

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

// expected object
const expectedObject= {
  a: '1 a',
  b: {
    c: '2 a',
    d: {
      e: '3 a',
      f: {
        g: '4 a'
      }
    }
  }
};

вот мой пример кода:

let expectedObject = {};
const newObject = object => {
  Object.entries(object).forEach(([key, value]) => {
    if (typeof value === "object") {
      Object.keys(value).map(key => {
        value[key] = value[key] + " a";
        return value;
      });

      expectedObject[key] = value;
      //return newTest;
    } else {
      expectedObject[key] = value;
      return expectedObject;
    }
  });
  return expectedObject;
};
console.log(newObject(testObj));

результат в консоли:

{a: 1, b: {…}}
a: 1
b:
c: "2 a"
d: "[object Object] a"
__proto__: Object
__proto__: Object

Я хотел использовать рекурсию здесь и тоже попробовал, но не повезло. любая помощь, спасибо?

Ответы [ 4 ]

6 голосов
/ 01 октября 2019

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

function map(object, fn) {
    return Object.fromEntries(Object
        .entries(object)
        .map(([k, v]) => [k, v && typeof v === 'object' ? map(v, fn) : fn(v)])
    );
}

var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } },
    result = map(object, v => v + ' a');

console.log(result);

Если у вас есть массивы внутри, вы можете заранее добавить чек и отобразить значения.

const
    map = fn => {
        const iter = v => v && typeof v === 'object'
            ? Array.isArray(v)
                ? v.map(iter)
                : Object.fromEntries(Object.entries(v).map(([k, v]) => [k, iter(v, fn)]))
            : fn(v);
        return iter;
    };

var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4, k: [5, 6] } } } },
    addA = map(v => v + ' a'),
    result = addA(object);

console.log(result);
2 голосов
/ 01 октября 2019

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

const map = (f) => (a) => 
  a.map(f)

const mapObj = (f) => (o) => 
  Object .entries (o) .reduce ( (a, [k, v] ) => ({ ...a, [k]: f(v) }), {})

const traverse = (f) => (t) =>
  Array.isArray(t)
    ? map (traverse (f)) (t)
  : Object(t) === t 
    ? mapObj (traverse (f)) (t)
  : f (t)

const input =
  { a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }

const output =
  traverse(x => `${x} a`) (input)

console.log(output)

mapObj можно записать разными способами. Вот две альтернативы:

const mapObj = (f = identity) => (o = {}) => 
  Object .fromEntries (Object .entries (o) .map (([ k, v ]) => [ k, f (v) ]))


const mapObj = (f = identity) => (o = {}) => 
  Object .assign .apply (null, Object .entries (o) .map (([ k, v ]) => ({ [k]: f (v) 
1 голос
/ 01 октября 2019

Вот простая рекурсивная техника. Он похож на Nina, но сохраняет массивы, если они присутствуют в структуре.

  1. Если вход, t, является массивом, создайте новый массив, пройдя через каждое значение массива, vс функцией перемещения f
  2. (индуктивный) В противном случае t - это , а не массив. Если t является объектом, создайте новый объект из пар ключ-значение, [ k, v ], пройдя каждое значение, v, с помощью функции перемещения, f
  3. (индуктивная) В противном случае t это не массив, а t это не объект. Это означает, что t является либо примитивным значением, таким как строка, число или null

Нумерованные комментарии ниже соответствуют объяснению выше -

const identity = x =>
  x

const traverse = (f = identity, t = {}) =>
  Array.isArray(t)                         // 1
    ? Array.from(t, v => traverse(f, v))
: Object(t) === t                          // 2
    ? Object.fromEntries(Object.entries(t).map(([ k, v ]) => [ k, traverse(f, v) ]))
: f (t)                                    // 3

const input =
  { a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }

const output =
  traverse(x => `${x} a`, input)

console.log(output)
0 голосов
/ 01 октября 2019

Вот подход, использующий модификацию исходного кода для демонстрации того, что необходимо изменить, чтобы заставить его работать. Вы переключились на чтение value и установку нового. Также я использую оператор распространения, чтобы клонировать объект перед его изменением.

const testObj = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3,
      f: {
        g: 4
      }
    }
  }
};

const newObject = object => {
  const clonedObj = { ...object };
  const entries = Object.entries(clonedObj);

  entries.forEach(([key, value]) => {
    if (typeof value === "object") {
      clonedObj[key] = newObject(value);
    } else {
      clonedObj[key] = value + " a";
    }
  });
  return clonedObj;
};
console.log(newObject(testObj));
console.log(testObj); // prove that the original object hasn't changed
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...