Конвертируйте произвольно глубоко вложенное значение в ключ объекта, используя Ramda или vanillaJS - PullRequest
1 голос
/ 01 марта 2020

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

red.walk.fast.true становится {red: {walk: {fast: 'true'}}} или climbing.ascending становится {climbing: 'ascending'}

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

{red: {walk: {fast: 'true'}}} становится {red: {walk: {fast: {true: null}}}}

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

const stringPropToObject = R.ifElse(
    R.is(String),
    R.assoc(R.__, null, {}),
    mapAllStringPropsToObjs);

const mapAllStringPropsToObjs = R.map(stringPropToObject)

const updatedObject = mapAllStringPropsToObjs({ a: { b: { c: 'd' } } })

console.log(updatedObject);

mapAllStringPropsToOb js (попытки) сопоставить свойства объектов, передавая их stringPropToObject . Затем он просматривает переданное свойство.

  • Если это строка, то он возвращает новый объект со свойством с таким именем строки.
  • Если это объект, он ( должен) рекурсивно вызывать mapAllStringPropsToOb js, передавая объект до тех пор, пока не будет достигнуто значение String.

Это приводит к «Невозможно получить доступ к mapAllStringPropsToOb js 'до инициализации». Я понимаю, почему я получаю эту ошибку с текущим порядком объявлений, но я не понимаю, как можно их упорядочить, чтобы избежать этого - они являются взаимозависимыми функциями.

Кто-нибудь знает, что я мог бы сделать, используя Ramda или vanilla JS, чтобы иметь возможность преобразовать: {climbing: 'ascending'} в {climbing: {ascending: null}} и {red: {walk: {fast: 'true'}}} в {red: {walk: {fast: {true: null}}}} или любое другое произвольно вложенное значение.

Заранее спасибо за любые предложения.

Ответы [ 4 ]

1 голос
/ 02 марта 2020

Как заявлено @ScottSauyet, вы можете использовать функцию (анонимную или стрелку) для вызова mapAllStringPropsToObjs() в будущем, когда она действительно определена.

Часть кода, которая выполняет отложенные вычисления, называется thunk , Согласно wikipedia :

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

Кроме того, я бы также заменил R.asso c с перевернутым R.objOf для создания объекта.

const objOfNull = flip(objOf)(null);

const mapAllStringPropsToObjs = map(ifElse(
  is(String),
  objOfNull,
  x => mapAllStringPropsToObjs(x)
));

const updatedObject = mapAllStringPropsToObjs({ a: { b: { c: 'd' } } })

console.log (updatedObject)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {map, ifElse, is, flip, objOf} = R                               </script>
1 голос
/ 02 марта 2020

Самый простой способ исправить проблему с зависимыми функциями - просто заменить ссылку в первой на вторую лямбду:

const stringPropToObject = ifElse(
  is(String),
  assoc(__, null, {}),
  x => mapAllStringPropsToObjs(x)
);

const mapAllStringPropsToObjs = map(stringPropToObject)


const updatedObject = mapAllStringPropsToObjs({ a: { b: { c: 'd' } } })

console.log (updatedObject)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {ifElse, is, assoc, __, map} = R                               </script>

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

Я, вероятно, написал бы это по-другому, в ванили JS, с чем-то вроде этого:

const transform = (obj) => 
  Object .entries (obj) 
    .reduce ((a, [k, v]) => ({
      ...a,
      [k]: typeof v == 'string' ? {[v]: null} : transform (v)
    }), {})

Но я думаю, что ваше рекурсивное решение Ramda с этим небольшим изменением проще и чище.

1 голос
/ 01 марта 2020

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

function f(object) {
    Object.entries(object).forEach(([k, v]) => {
        if (v && typeof v === 'object') f(v);
        else object[k] = { [v]: null };
    });
}

var object = { red: { walk: { fast: 'true' } } };

f(object);
console.log(object);
0 голосов
/ 01 марта 2020

Это решение является рекурсивным и создает новый преобразованный объект вместо изменения старого.

function transformObject(obj, final = {}, curr = final) {
  Object.entries(obj).forEach(([key, value]) => {
    curr[key] = {}
    if (typeof value !== 'object') curr[key][value] = null
    else transformObject(value, final, curr[key])
  })
  return final
}

const dataOne = {climbing: {run: 'slow', hike: {mountain: 'fast'}}}
const dataTwo = {red: {walk: {fast: 'true'}}}

console.log(dataOne, transformObject(dataOne))
console.log(dataTwo, transformObject(dataTwo))
...