将嵌套列表转换为对象 - Javascript

Transform nested list into object - Javascript

我们需要帮助来解决这个问题,我们企业中没有人能够做到。

我们有这样一个字符串:

- name
    - type
        - string
    - validation
        - required
        - minLength
            - 4
        - maxLength
            - 20
        - optionIn
            - option1
            - option2
            - option3
            - option4
- password
    - type
        - string
    - validation
        - required
        - minLength
            - 6
        - maxLength
            - 30
- date
    - type
        - date

并且我们需要生成这样一个对象:

{
   name: {
      type: 'string',
      validation: {
        required: true,
        minLength: 4,
        maxLength: 20,
        optionIn: ['option1', 'option2', 'option3', 'option4']
      }
    },
    password: {
      type: 'string',
      validation: {
        required: true,
        minLength: 6,
        maxLength: 30
      }
    },
    date: {
      type: 'date'
    }
}

使这成为一项真正复杂的任务的一些因素:

如果最后一个嵌套项只有一个,那么他就是上一个键的值。如果最后的嵌套项不止一个,则变成一个数组,数组就是前一个key的值。

编辑: 感谢@adiga 的洞察力,示例 'required' 变成一个值为 true 的对象,因为他的伙伴有一个嵌套项

这是一项艰巨而复杂的任务,如果需要,可以使用图书馆。

我提出的解决方案分两步进行。

首先,我 parse()inputStr 以最简单的方式转换为中间形式,最终看起来像这样:

{
    "name": {
        "type": {
            "string": null
        },
        "validation": {
            "required": null,
            "minLength": {
                "4": null
            },
            "maxLength": {
                "20": null
            },
            "optionIn": {
                "option1": null,
                "option2": null,
                "option3": null,
                "option4": null
            }
        }
    },
    "password": {
        "type": {
            "string": null
        },
        "validation": {
            "required": null,
            "minLength": {
                "6": null
            },
            "maxLength": {
                "30": null
            }
        }
    },
    "date": {
        "type": {
            "date": null
        }
    }
}

然后我transform()将那个中间对象变成最终形式。

const inputStr = 
`- name
    - type
        - string
    - validation
        - required
        - minLength
            - 4
        - maxLength
            - 20
        - optionIn
            - option1
            - option2
            - option3
            - option4
- password
    - type
        - string
    - validation
        - required
        - minLength
            - 6
        - maxLength
            - 30
- date
    - type
        - date`

let parseLimit = 1000;
function parse(lines, curIndent = 0) {
 if (parseLimit-- < 0) throw "parseLimit exhausted";
 if (lines.length === 0) return null;
 
 const obj = {};
 let parent = null;
 let descendantLines = [];
 [...lines, '>'.repeat(curIndent)].forEach(line => {
  const indents = (line.match(/>/g) || []).length;
  if (indents === curIndent) {
   if (parent) {
    obj[parent] = parse(descendantLines, curIndent + 1);
   }
   descendantLines = [];
   parent = line.replace(/>/g, '');
  } else if (indents > curIndent) {
   descendantLines.push(line);
  } else {
   throw 'indents < curIndent';
  }
 });
 
 
 return obj;
}

let transformLimit = 1000;
function transform(node) {
 if (transformLimit-- < 0) throw "transformLimit exhausted";
 
 const childKeys = Object.keys(node);
 const leafChildKeys = childKeys.filter(childKey => {
  return node[childKey] === null;
 });
 if (childKeys.length === leafChildKeys.length) {
  //all leaf children
  const values = childKeys.map(value => {
   return isNaN(value)
    ? value
    : +value;
  });
  return values.length === 1
   ? values[0]
   : values;
 } else { //not all leaf children 
  const newNode = {};
  
  childKeys.forEach(childKey => {
   if (leafChildKeys.includes(childKey)) {
    //true
    newNode[childKey] = true;
   } else {
    //recurs
    newNode[childKey] = transform(node[childKey]);
   }
  });
  
  return newNode;
 }
}

function solve(str) {
 const lines = str
  .split('\n')
  .map(line => line
   .replace(/    /g, '>')
   .replace('- ', '')
  );
 return transform(parse(lines));
}

console.log('input:\n', inputStr);
console.log('solution: ', solve(inputStr));