将字符串列表转换为 recursive/nested dict/json 对象(文件资源管理器类型系统)
convert list of strings to recursive/nested dict/json object (file explorer type system)
我有一个 Redux 商店,它目前正在向我返回一个字符串列表,这些字符串代表下面的文件夹示例
/Documents/Pictures/Test
/Documents/Pictures/Test2
/System
/Libraries/Node
我想将其覆盖到 javascript 中的 dict/json 对象,这样它看起来像:
data = { id:'0',
name:"/",
child:[{
id:'1',
name:"Documents",
child:[
id:'2',
name:"Pictures",
child:[{
id:'3',
name:"Test",
child:[]
},
{
id:'3',
name:"Test2",
child:[]
}]
]},
id:'1',
name:"System",
child:[]
]}
.... so on and so forth
我在这上面花了两天时间,我也尝试过使用 Object.keys 但似乎无法让它工作和匹配但似乎无法让它工作。
这是我当前“有效”但不能递归执行的代码
convertThePaths(){
var pathRow = this.props.paths[0]; //grabbing the first array to test (/documents/pictures/test) is in here
pathArray = pathRow.split("/") //splits between the /'s to get folder names
var data = { id:'0',name:"/",child:[]}
for (var j = 0; pathArray.length;j++){
data.child.push(
id: j
name: pathArray[j],
child: [] // need recursive here? maybe call convertPaths?
)
}
id 并不重要,只要它是唯一的,我知道如何在 python 但不是 javascript 中做到这一点。任何帮助表示赞赏!我觉得我把这个复杂化了......
下面是操作方法。我用代码注释解释
const convertThePaths = (pathRows,data,index)=>{
// Once index is the size of the pathRows were done
if (pathRows.length===index){
return;
}
let pathArray = pathRows[index].split("/"); //splits between the /'s to get folder names
pathArray = pathArray.slice(1,pathArray.length); // remove empty string at start of arr.
// Cur depth of dictionary
let curData = data;
// Loop through all paths in pathArray
for (const pathName of pathArray){
// Filter for the pathName in the children
let childInlst = curData["child"].filter(obj=>obj.name===pathName);
// If list is empty no such child was found so add it
if (childInlst.length===0){
// Add child that doesn't exist yet
curData["child"].push({
"name":pathName,
"id":`${parseInt(curData["id"])+1}`,
"child":[],
})
}
// Now child must exist so filter it and it will be index 0 of the list
curData = curData["child"].filter(obj=>obj.name===pathName)[0];
// Then repeat
}
// Now repeat the same step with the next path in pathRows
convertThePaths(pathRows,data,index+1);
}
// Prints out the data recursively
const printData = (data)=>{
console.log(data.name+" "+data.id);
if (data.child.length===0){
return;
}
for (const child of data.child){
printData(child)
}
}
const Main=()=>{
const pathRows = [
"/Documents/Pictures/Test",
"/Documents/Pictures/Test2",
"/System",
"/Libraries/Node",
]
// Data start state
const data = {"id":"0","name":"/","child":[]};
// Recursive function
convertThePaths(pathRows,data,0);
// function to print data out recursively
printData(data);
}
Main();
递归打印输出
/ 0
Documents 1
Pictures 2
Test 3
Test2 3
System 1
Libraries 1
Node 2
有些像这样:
function getMyData(list) {
const data = { id: "0", name: "/", child: [] };
for (let a = 0; a < list.length; a++) {
const pathArray = list[a].split("/").splice(1);
if (pathArray.length) {
for (let i = 0; i < pathArray.length; i++) {
const subChild = {
id: (i + 1).toString(),
name: pathArray[i],
child: [],
};
data.child.push(subChild);
if (pathArray.length > i) {
const currentChildIndex = data.child.findIndex(
(x) => x.name === pathArray[i]
);
const newObj = {
id: (i + 2).toString(),
name: pathArray[i + 1],
child: [],
};
if (currentChildIndex === 0 || currentChildIndex) {
data.child[currentChildIndex].child.push(newObj);
}
}
pathArray.splice(i);
}
}
}
return data;
}
console.log(getMyData(arr));
但我认为可能有更好的方法
这是一种基于我used elsewhere:
的相当通用的函数的方法
const setPath = ([p, ...ps], id = 0) => (v) => (o) =>
p == undefined ? v : Object .assign (
Array .isArray (o) || Number .isInteger (p) ? [] : {},
{...o, [p]: setPath (ps, id + 1) (v) ((o || {}) [p])},
)
const expand = (o, id = 0) =>
Object .entries (o) .map (([name, v]) => ({id, name, child: expand (v, id + 1)}))
const transform = (ps) =>
expand (
ps .map (p => ['/', ...p .slice (1) .split ('/')])
.reduce ((a, p) => setPath (p) ({}) (a), {})
) [0]
const paths = ['/Documents/Pictures/Test', '/Documents/Pictures/Test2', '/System', '/Libraries/Node']
console .log (transform (paths))
.as-console-wrapper {max-height: 100% !important; top: 0}
通用效用函数 setPath
接受诸如 ['foo', 'bar', 'baz']
的路径,然后接受诸如 42
的值,最后接受诸如 {foo : {qux: 100, corge: 200})
的 object ,以及 returns 和 object 类似 {foo : {qux: 100, corge: 200, bar: {baz: 42})
。如果缺少中间路径,它会创建它们:如果路径节点是整数,则创建数组,否则 objects。
主要功能是transform
,其中
获取您的条目数组,例如 '/Documents/Pictures/Test'
,并将每个条目变成类似于 ['/', 'Documents', 'Pictures', 'Test']
我们故意在每个结果的开头添加一个额外的 /
考虑到最终的根节点,也没有清楚地出现在原始节点中。
在这些结果上折叠 setPath
,为每个值传递一个空的 object,返回类似 {"/": {Documents: {Pictures: {Test: {}, Test2: {}}}, System: {}, Libraries: {Node: {}}}}
的内容
调用 expand
助手,它(递归地)将其(几乎)转换为您正在寻找的结构,只是包装在一个数组中,因为递归需要处理children.
的数组
从返回的数组中提取第一个值以提供最终结果。
我发现这种转变,作为一系列不同的步骤更容易思考,而且每个步骤都相当简单,整个事情可以变得更容易编写。
我有一个 Redux 商店,它目前正在向我返回一个字符串列表,这些字符串代表下面的文件夹示例
/Documents/Pictures/Test
/Documents/Pictures/Test2
/System
/Libraries/Node
我想将其覆盖到 javascript 中的 dict/json 对象,这样它看起来像:
data = { id:'0',
name:"/",
child:[{
id:'1',
name:"Documents",
child:[
id:'2',
name:"Pictures",
child:[{
id:'3',
name:"Test",
child:[]
},
{
id:'3',
name:"Test2",
child:[]
}]
]},
id:'1',
name:"System",
child:[]
]}
.... so on and so forth
我在这上面花了两天时间,我也尝试过使用 Object.keys 但似乎无法让它工作和匹配但似乎无法让它工作。
这是我当前“有效”但不能递归执行的代码
convertThePaths(){
var pathRow = this.props.paths[0]; //grabbing the first array to test (/documents/pictures/test) is in here
pathArray = pathRow.split("/") //splits between the /'s to get folder names
var data = { id:'0',name:"/",child:[]}
for (var j = 0; pathArray.length;j++){
data.child.push(
id: j
name: pathArray[j],
child: [] // need recursive here? maybe call convertPaths?
)
}
id 并不重要,只要它是唯一的,我知道如何在 python 但不是 javascript 中做到这一点。任何帮助表示赞赏!我觉得我把这个复杂化了......
下面是操作方法。我用代码注释解释
const convertThePaths = (pathRows,data,index)=>{
// Once index is the size of the pathRows were done
if (pathRows.length===index){
return;
}
let pathArray = pathRows[index].split("/"); //splits between the /'s to get folder names
pathArray = pathArray.slice(1,pathArray.length); // remove empty string at start of arr.
// Cur depth of dictionary
let curData = data;
// Loop through all paths in pathArray
for (const pathName of pathArray){
// Filter for the pathName in the children
let childInlst = curData["child"].filter(obj=>obj.name===pathName);
// If list is empty no such child was found so add it
if (childInlst.length===0){
// Add child that doesn't exist yet
curData["child"].push({
"name":pathName,
"id":`${parseInt(curData["id"])+1}`,
"child":[],
})
}
// Now child must exist so filter it and it will be index 0 of the list
curData = curData["child"].filter(obj=>obj.name===pathName)[0];
// Then repeat
}
// Now repeat the same step with the next path in pathRows
convertThePaths(pathRows,data,index+1);
}
// Prints out the data recursively
const printData = (data)=>{
console.log(data.name+" "+data.id);
if (data.child.length===0){
return;
}
for (const child of data.child){
printData(child)
}
}
const Main=()=>{
const pathRows = [
"/Documents/Pictures/Test",
"/Documents/Pictures/Test2",
"/System",
"/Libraries/Node",
]
// Data start state
const data = {"id":"0","name":"/","child":[]};
// Recursive function
convertThePaths(pathRows,data,0);
// function to print data out recursively
printData(data);
}
Main();
递归打印输出
/ 0
Documents 1
Pictures 2
Test 3
Test2 3
System 1
Libraries 1
Node 2
有些像这样:
function getMyData(list) {
const data = { id: "0", name: "/", child: [] };
for (let a = 0; a < list.length; a++) {
const pathArray = list[a].split("/").splice(1);
if (pathArray.length) {
for (let i = 0; i < pathArray.length; i++) {
const subChild = {
id: (i + 1).toString(),
name: pathArray[i],
child: [],
};
data.child.push(subChild);
if (pathArray.length > i) {
const currentChildIndex = data.child.findIndex(
(x) => x.name === pathArray[i]
);
const newObj = {
id: (i + 2).toString(),
name: pathArray[i + 1],
child: [],
};
if (currentChildIndex === 0 || currentChildIndex) {
data.child[currentChildIndex].child.push(newObj);
}
}
pathArray.splice(i);
}
}
}
return data;
}
console.log(getMyData(arr));
但我认为可能有更好的方法
这是一种基于我used elsewhere:
的相当通用的函数的方法const setPath = ([p, ...ps], id = 0) => (v) => (o) =>
p == undefined ? v : Object .assign (
Array .isArray (o) || Number .isInteger (p) ? [] : {},
{...o, [p]: setPath (ps, id + 1) (v) ((o || {}) [p])},
)
const expand = (o, id = 0) =>
Object .entries (o) .map (([name, v]) => ({id, name, child: expand (v, id + 1)}))
const transform = (ps) =>
expand (
ps .map (p => ['/', ...p .slice (1) .split ('/')])
.reduce ((a, p) => setPath (p) ({}) (a), {})
) [0]
const paths = ['/Documents/Pictures/Test', '/Documents/Pictures/Test2', '/System', '/Libraries/Node']
console .log (transform (paths))
.as-console-wrapper {max-height: 100% !important; top: 0}
通用效用函数 setPath
接受诸如 ['foo', 'bar', 'baz']
的路径,然后接受诸如 42
的值,最后接受诸如 {foo : {qux: 100, corge: 200})
的 object ,以及 returns 和 object 类似 {foo : {qux: 100, corge: 200, bar: {baz: 42})
。如果缺少中间路径,它会创建它们:如果路径节点是整数,则创建数组,否则 objects。
主要功能是transform
,其中
获取您的条目数组,例如
'/Documents/Pictures/Test'
,并将每个条目变成类似于['/', 'Documents', 'Pictures', 'Test']
我们故意在每个结果的开头添加一个额外的/
考虑到最终的根节点,也没有清楚地出现在原始节点中。在这些结果上折叠
的内容setPath
,为每个值传递一个空的 object,返回类似{"/": {Documents: {Pictures: {Test: {}, Test2: {}}}, System: {}, Libraries: {Node: {}}}}
调用
的数组expand
助手,它(递归地)将其(几乎)转换为您正在寻找的结构,只是包装在一个数组中,因为递归需要处理children.从返回的数组中提取第一个值以提供最终结果。
我发现这种转变,作为一系列不同的步骤更容易思考,而且每个步骤都相当简单,整个事情可以变得更容易编写。