как превратить функцию flattenObj из поваренной книги ramda в итеративную функцию - PullRequest
0 голосов
/ 14 апреля 2019

Я имею дело с тестовой средой nodejs / sequelize / mocha / chai.Я нахожу это flattenObj чрезвычайно полезным при тестировании объектов, сгенерированных, например, с помощью sequelize.
Это делает эти структуры удобочитаемыми для chai, и результаты становятся более краткими
Жаль, что это реализовано рекурсивным способом:(.
Особенно в Javascript это означает обреченность, так как всегда существует скрытый предел стека вызовов.
Такие хаки, как оборачивание рекурсивной функции в setTimeout, не работают для меня и выглядят некрасиво.
В настоящее время я пытаюсь выяснить, переписать ли это итеративным способом, но это довольно сложная задача, по крайней мере, для меня.
Работа с циклами while внутри функции ramda не выглядит правильной.
IsЕсть ли способ сделать это дружественным для стека вызовов способом, не нарушая условных обозначений ramda?

const go = obj_ => chain(([k, v]) => {
  if (type(v) === 'Object' || type(v) === 'Array') {
    return pipe( 
        tap(console.log),            
        map(([k_, v_]) => [`${k}.${k_}`, v_]) 
     )(go(v))
  } else {
    return [[k, v]]
  }
}, toPairs(obj_))

const flattenObj = obj => {
  return fromPairs(go(obj))
}

flattenObj({a:1, b:{c:3}, d:{e:{f:6}, g:[{h:8, i:9}, 0]}})

{
  "a": 1,
  "b.c": 3,
  "d.e.f": 6,
  "d.g.0.h": 8,
  "d.g.0.i": 9,
  "d.g.1": 0
}

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

1 Ответ

1 голос
/ 14 апреля 2019

Я не думаю, что это плохо, что это реализовано рекурсивным способом.Это лучший способ справиться с рекурсивными структурами данных, такими как объекты JS.

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

const flattenObj = (obj) => {
  const results = [];
  const steps = Object.entries(obj)
  while (steps.length) {
    const [key, val] = steps.splice(0, 1)[0]
    if (typeof val == 'object') {
      Array.prototype.push.apply(steps, Object.entries(val).map(
        ([k, v]) => [key + '.' + k, v]
      ))
    } else {
      results.push([key, val])
    }
  }
  return results.reduce((a, [k, v]) => ({...a, [k]: v}), {})
}


const foo = {a:1, b:{c:3}, d:{e:{f:6}, g:[{h:8, i:9}, 0]}}

console.log(flattenObj(foo))

Это не будет работать с циклическими структурами, но версия кулинарной книги тоже не будет, предположительно.

Я написал это изначально, используя некоторые функции Ramda (toPairs вместо Object.entries, is(Object, val) вместо typeof val == 'object' и return fromPairs(results) вместо return results.reduce(...).) Но при всей происходящей мутации (splice и push) он чувствуеточень unRamda-иш решение, и я удалил их.(Вы понимаете, что функции Рамды не хотят ассоциироваться с изменчивостью!)

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

...