Как с помощью рекурсии преобразовать строку по точке в вложенные обьекты
Моя функция принимает следующего вида строку
'a.b.c.d.e'
на выходе я должен получить такой обьект
{"a":{"b":{"c":{"d":{"e":{}}}}}}
с помощью reduce данную задачу можно решить вот так
function namespace(s) {
return s.split('.').reverse().reduce((acc, a)=>{
let n = {};
return n[a] = acc, n
},{})
}
но как сделать это с помощью рекурсивного обхода?
Ответы (2 шт):
Если решать без функций-обёрток, только с одной рекурсивной функцией, принимающей строку, но можно, например, такими двумя способами:
const path = 'a.b.c.d.e';
function namespace1(path, current = {}, main = current) {
const fields = path.split('.');
const field = fields.shift();
current[field] = {};
if (fields.length === 0) return main;
return namespace1(fields.join('.'), current[field], main);
}
function namespace2(path, current = {}) {
const parent = {};
const fields = path.split('.');
const field = fields.pop();
parent[field] = current;
if (fields.length === 0) return parent;
return namespace2(fields.join('.'), parent);
}
console.log(JSON.stringify(namespace1(path)));
console.log(JSON.stringify(namespace2(path)));
Для написания рекурсивной функции нужно определиться как минимум с двумя вещами:
- условия выхода из рекурсии
- какие действия должны повторяться
Условия выхода из рекурсии
Первое что нужно сделать - определиться, когда нужно закончить рекурсию.
В данном случае очевидно, что если передали пустой путь - то не для чего запускать рекурсию. В этом случае надо просто вернуть либо пустой объект, который потом будет заворачиваться в объекты по мере выхода из рекурсии, либо накопленный результат.
Какие действия должны повторяться
Можно выделить повторяющиеся действия, а именно:
- получение ключа и создание объекта
- определение, что нужно передать на следующий шаг рекурсии
Например:
из задачи видно, что нужно создать объект, у которого ключ соответствует элементу массива
{ a: {...}}{ b: {...}}{ c: {...}}и так далее.
То есть, можно заметить, что если убрать первый элемент массива, то останется та же самая структура
{ b: {...}}{ c: {...}}
таким образом, вместо {...} можно записать рекурсивный вызов функции, передав ей тот же самый массив, без первого элемента.
например, можно воспользоваться деструктуризацией и сразу получить отдельно первый элемент и все элементы массива без первого
var [head, ...tail] = path;
далее достаточно создать объект
var obj = { [head]: fun(tail) }
и вернуть этот объект из функции.
Примеры реализации:
function fun(path) {
if (path.length == 0) return {}; // возвращаем пустой объект и выходим из рекурсии
var [key, ...rest] = path; // определяем текущий ключ
return { // возвращаем объект с нужным ключом и уходим в рекурсию.
[key]: fun(rest)
};
}
console.log(fun('a.b.c.d.e'.split('.')));
function fun(path, acc = {}) { // накапливаем результат в параметре `acc`
if (path.length == 0) return acc; // если пришел пустой путь - возвращаем накопленное значение
var key = path.pop(); // определяем текущий ключ
// рекурсивно вызываем функцию, передавая вторым параметром накопленный результат
return fun(path, {
[key]: acc
});
}
console.log(fun('a.b.c.d.e'.split('.')));
Стоит обратить внимание, что .pop меняет массив, к которому применяется, удаляя из него последний элемент. Поэтому дополнительных преобразований path перед передачей в рекурсивный вызов - не нужно.
Для того чтобы функция принимала строку, достаточно написать обертку:
function namespace(s) {
return fun(s.split('.'));
}