Need to convert the array of file paths into Treeview JSON object
Array Data:
[path1/subpath1/file1.doc",
"path1/subpath1/file2.doc",
"path1/subpath2/file1.doc",
"path1/subpath2/file2.doc",
"path2/subpath1/file1.doc",
"path2/subpath1/file2.doc",
"path2/subpath2/file1.doc",
"path2/subpath2/file2.doc",
"path2/subpath2/additionalpath1/file1.doc"]
I want below object Result:
{
"title": "path1",
"childNodes" : [
{ "title":"subpath1", "childNodes":[{ "title":"file1.doc", "childNodes":[] }] },
{ "title":"subpath2", "childNodes":[{ "title":"file1.doc", "childNodes":[] }] }
]
}
I was able to convert it into an object using the below code snippet but not able to transform the way I want it
let treePath = {};
let formattedData = {};
data.forEach(path => {
let levels = path.split("/");
let file = levels.pop();
let prevLevel = treePath;
let prevProp = levels.shift();
levels.forEach(prop => {
prevLevel[prevProp] = prevLevel[prevProp] || {};
prevLevel = prevLevel[prevProp];
prevProp = prop;
});
prevLevel[prevProp] = (prevLevel[prevProp] || []).concat([file]);
});
How can i do this????
You could reduce the parts of pathes and search for same title.
const
pathes = ["path1/subpath1/file1.doc", "path1/subpath1/file2.doc", "path1/subpath2/file1.doc", "path1/subpath2/file2.doc", "path2/subpath1/file1.doc", "path2/subpath1/file2.doc", "path2/subpath2/file1.doc", "path2/subpath2/file2.doc", "path2/subpath2/additionalpath1/file1.doc"],
result = pathes.reduce((r, path) => {
path.split('/').reduce((childNodes, title) => {
let child = childNodes.find(n => n.title === title);
if (!child) childNodes.push(child = { title, childNodes: [] });
return child.childNodes;
}, r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
Say I have the following strings:
"files/photos/foo.png"
"files/videos/movie.mov"
and I want to convert them to the following object:
{
name: "files"
children: [{
name: "photos",
children: [{
name: "foo.png",
id: "files/photos/foo.png"
}]
},{
name: "videos",
children: [{
name: "movie.mov",
id: "files/videos/movie.mov"
}]
}]
}
What would be the best approach for doing so? I've tried writing some recursive functions, however admit that I'm struggling at the moment.
Here's a quick snippet with a possible solution. It uses nested loops, the outer splitting each path by the delimeter and pop()ing the file portion out of the array. The inner iterates the parts of the path and constructs the heirarchy by reasigning branch on each iteration. Finally the file portion of the path is added to the deepest branch.
const data = [
'files/photos/foo.png',
'files/photos/bar.png',
'files/videos/movie.mov',
'docs/photos/sd.jpg'
];
const tree = { root: {} }
for (const path of data) {
const parts = path.split('/');
const file = parts.pop();
let branch = tree, partPath = '';
for (const part of parts) {
partPath += `${part}/`;
if (partPath === `${part}/`) {
tree.root[partPath] = (tree[partPath] ??= { name: part, children: [] });
} else if (tree[partPath] === undefined) {
tree[partPath] = { name: part, children: [] };
branch.children.push(tree[partPath]);
}
branch = tree[partPath];
}
branch.children.push({ name: file, id: path });
}
const result = Object.values(tree.root)
console.log(JSON.stringify(result, null, 2))
.as-console-wrapper { max-height: 100% !important; top: 0; }
.as-console-row::after { display: none !important; }
Or as a function.
function mergeAssets(assets) {
const tree = { root: {} }
for (const path of data) {
const parts = path.split('/');
const file = parts.pop();
let branch = tree, partPath = '';
for (const part of parts) {
partPath += `${part}/`;
if (partPath === `${part}/`) {
tree.root[partPath] = (tree[partPath] ??= { name: part, children: [] });
} else if (tree[partPath] === undefined) {
tree[partPath] = { name: part, children: [] };
branch.children.push(tree[partPath]);
}
branch = tree[partPath];
}
branch.children.push({ name: file, id: path });
}
return {
name: "assets",
children: Object.values(tree.root)
}
}
const data = [
'files/photos/foo.png',
'files/photos/bar.png',
'files/videos/movie.mov',
'docs/photos/sd.jpg'
];
const result = mergeAssets(data);
console.log(JSON.stringify(result, null, 2))
I was able to find a solution using a recursive function. If others have any tips on how to improve this, I'd love to hear.
function mergeObjects(parentArray,path,originalName){
if(originalName === undefined){
originalName = path;
}
const parts = path.split("/");
var nextPart = "";
parts.forEach((part, index) => index > 0 ? nextPart += (nextPart !== "" ? "/" : "") + part : null);
//does the parentArray contain a child with our name?
const indexOfChild = parentArray.findIndex(child => child.name === parts[0]);
if(indexOfChild === -1){
//this item does not exist
if(parts.length > 1){
var index = parentArray.push({
name: parts[0],
children : []
}) - 1;
mergeObjects(parentArray[index].children,nextPart,originalName);
}else{
parentArray.push({
name: parts[0],
id : originalName
});
}
}else{
//this item already exists
if(parts.length > 1){
mergeObjects(parentArray[indexOfChild].children,nextPart,originalName);
}
}
}
And the function is called with the following:
function mergeAssets(assets){
var obj = {
name: "assets",
children: []
};
assets.forEach(asset => mergeObjects(obj.children,asset));
return obj;
}
how to find the json path for a specific value using javascript
var data = {
key1: {
children: {
key2:'value',
key3:'value',
key4: value
},
key5: 'value'
}
expected result from the above data.key1.children.key3
Any help would be appreciated.
var str = "key3";
data["key1"]["children"][str];
This function returns all available paths in your(any) object:
Using this function you can get
["key1.children.key2", "key1.children.key3", "key1.children.key4", "key1.key5"]
function allPaths(root) {
let stack = [];
let result = [];
// checks if object
const isObject = value => typeof value === "object";
stack.push(root);
while (stack.length > 0) {
let node = stack.pop();
if (isObject(node)) {
Object.entries(node).forEach(([childNodeKey, childNodeValue]) => {
if (isObject(childNodeValue)) {
const newObject = Object.fromEntries(
Object.entries(childNodeValue).map(([cnk, cnv]) => {
return [`${childNodeKey}.${cnk}`, cnv];
})
);
stack.push(newObject);
} else {
stack.push(`${childNodeKey}`);
}
})
} else {
result.push(node);
}
}
return result.reverse();
}
Let me know in the comments if it was helpful for you
I need to update the object name based on the array of the string value and the last string value should be an array.
I use array.forEach loop but I don't know how to find the object inside an object if it exists and the myArray contain around 10,000 strings.
const myArray = [
'/unit/unit/225/unit-225.pdf',
'/nit/nit-dep/4.11/nit-4.11.pdf',
'/nit/nit-dep/4.12/nit-4.12.pdf',
'/org/viti/viti-engine/5.1/viti-engine-5.1.pdf',
'/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'
];
var parentObject = {}
myArray.forEach(res => {
res = res.slice(1, res.length);
var array = res.split("/");
array.forEach((e, i) => {
........ // here I am confused
});
})
final output should be
parentObject = {
'unit': {
'unit': {
'225': {
'unit-225.pdf': []
}
}
},
'nit': {
'nit-dep': {
'4.11': {
'nit-4.11.pdf': []
},
'4.12': {
'nit-4.12.pdf': []
}
}
},
'org': {
'viti': {
'viti-engine': {
'5.1': {
'viti-engine-5.1.pdf': []
}
},
'viti-spring': {
'5.2': {
'viti-engine-5.2.pdf': []
}
}
}
}
}
Once you've split by slashes, use reduce to iterate to the nested object, creating each nested property first if necessary, then assign an array to the filename property:
const myArray = [
'/unit/unit/225/unit-225.pdf',
'/nit/nit-dep/4.11/nit-4.11.pdf',
'/nit/nit-dep/4.12/nit-4.12.pdf',
'/org/viti/viti-engine/5.1/viti-engine-5.1.pdf',
'/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'
];
var parentObject = {}
myArray.forEach((str) => {
const props = str.slice(1).split('/');
const filename = props.pop();
const lastObj = props.reduce((a, prop) => {
if (!a[prop]) {
a[prop] = {};
}
return a[prop];
}, parentObject);
lastObj[filename] = [];
});
console.log(parentObject);
You could reduce the array an reduce the path as well. At the end assign the array.
const
array = ['/unit/unit/225/unit-225.pdf', '/nit/nit-dep/4.11/nit-4.11.pdf', '/nit/nit-dep/4.12/nit-4.12.pdf', '/org/viti/viti-engine/5.1/viti-engine-5.1.pdf', '/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'],
result = array.reduce((r, path) => {
var keys = path.split(/\//).slice(1),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, r)[last] = [];
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A slightly faster approach.
const
array = ['/unit/unit/225/unit-225.pdf', '/nit/nit-dep/4.11/nit-4.11.pdf', '/nit/nit-dep/4.12/nit-4.12.pdf', '/org/viti/viti-engine/5.1/viti-engine-5.1.pdf', '/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'],
result = {};
for (let path of array) {
let keys = path.split(/\//).slice(1),
last = keys.pop(),
temp = result;
for (let key of keys) {
temp[key] = temp[key] || {};
temp = temp[key];
}
temp[last] = [];
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could also take a recursive approach.
Just keep shifting the split-up path until you get the final assignment for each branch.
const myArray = [
'/unit/unit/225/unit-225.pdf',
'/nit/nit-dep/4.11/nit-4.11.pdf',
'/nit/nit-dep/4.12/nit-4.12.pdf',
'/org/viti/viti-engine/5.1/viti-engine-5.1.pdf',
'/org/viti/viti-spring/5.1/viti-spring-5.1.pdf'
];
console.log(buildTree(myArray));
function buildTree(list=[]) {
return list.reduce((node, item) => buildBranches(node, item.split(/\//g).filter(x => x !== '')), {});
}
function buildBranches(node={}, rest=[]) {
let key = rest.shift();
node[key] = rest.length < 2 ? { [rest.shift()] : [] } /** or rest.shift() */ : node[key] || {};
if (rest.length > 1) buildBranches(node[key], rest);
return node;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
So heres the problem and heres the fiddle: https://jsfiddle.net/6zqco0mj/
const start = [{'a':'b'}, {'b':'c'}, {'c':'d'}, {'d':'e'}]
end =
{a:
{b:
{c:
{
d: {}
}
}
}
}
I have some code but not sure how would I dig deeper into an object
const start = [{'b':'c'}, {'a':'b'}, {'c':'d'}, {'d':'e'}];
const end = {};
function convert(key) {
const obj = getObj(key);
if(obj) {
const temp = {};
temp[obj[key]] = convert(obj[key]);
//findKey(obj[key]);
end[key] = temp;
}
}
function getObj(key) {
const foo = start.find((el, i) => { if(el[key]) { return el[key] } });
return foo;
}
function findKey(k) {
// what goes here?
}
convert('a');
console.log(end);
i think you went in the other way around for your aproach of recursivity; i gave it a try and this is what i got so far
const start = [{'b':'c'}, {'a':'b'}, {'c':'d'}, {'d':'e'}];
function convert(key, object) {
const obj = getObj(key);
if(obj) {
object[obj[key]] = {};
convert(obj[key], object[obj[key]]);
}
}
function getObj(key) {
const foo = start.find((el, i) => { if(el[key]) { return el[key] }});
return foo;
}
const end = { a: {}};
convert('a', end.a);
console.log(end);
You could use an object structure for collecting the data and build the tree.
This solutiotion works with unsorted data.
For getting the root properties, all keys and values are collected and only the subset is taken.
var data = [{ d: 'e' }, { b: 'c' }, { c: 'd' }, { a: 'b' }, { b : 'z' }],
tree = function (data) {
var keys = new Set,
values = new Set,
r = Object.create(null);
data.forEach(function (o) {
var [key, value] = Object.entries(o)[0];
keys.add(key);
values.add(value);
r[value] = r[value] || {};
r[key] = r[key] || {};
r[key][value] = r[key][value] || r[value];
});
values.forEach(v => keys.delete(v));
return Object.assign(...Array.from(keys, k => ({ [k]: r[k] })));
}(data);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have data in the form of
data = [
{
"date":"2018-05-18T-6:00:00.000Z",
"something":"something1",
"something":"something1"
},
{
"date":"2018-05-19T-6:00:00.000Z",
"something":"something2",
"something":"something2"
}
]
How do I grab the first element in the objects, edit them, then replace them back in the object?
So it should look like this
data = [
{
"date":"2018-05-18",
"something":"something1",
"something":"something1"
}
{
"date":"2018-05-19",
"something":"something2",
"something":"something2"
}
]
I have tried something like this
var date = [];
const getSessions = () => {
loginService.getUser().then((response) => {
var user_id = response.data.id;
console.log("getUser returning this => ", response.data);
loginService.getUserSessions(user_id).then((response) => {
$scope.sessions = response.data;
for (var i = 0; i < $scope.sessions.length; i++){
date.push($scope.sessions[i].next_class.slice(0,10));
};
$scope.sessions.push(date);
console.log($scope.sessions);
This gets the date shortened but doesn't replace the original date in the object.
You can do something like -
var data = [
{
"date":"2018-05-18T-6:00:00.000Z",
"something":"something1",
},
{
"date":"2018-05-19T-6:00:00.000Z",
"something":"something2"
}
]
data.forEach((record) => {
record.date = record.date.split("T")[0]
})
console.log(data);
You can do this also.
`
newArray = data.map(obj => {
dateIntoString = moment(obj.date).format('YYYY-MM-DD');
obj.date = dateIntoString;
return obj;
});
`