将嵌套列表转换为对象 - 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));
我们需要帮助来解决这个问题,我们企业中没有人能够做到。
我们有这样一个字符串:
- 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));