Parse URL-like data to json in javascript - javascript

Can anyone show me how to parse an array of URL-like data to json?
Array ["a.b.c.d","a.c.e.f","a.b.c.g"] to this kind of json:
items:{
text: "a",
items:[
{
text:"b",
items:[
{
text:"c",
items:[
{
text:"d",
leaf:true
},
{
text:"g",
leaf:true
}
]
}
]
},
{
text:"c",
items:[
{
text:"e",
items:[
{
text:"f",
leaf:true
}
]
}
]
}
]
}

The following should work:
// ['a', 'b'] -> { text: 'a', items: [{ text: 'b', leaf: true }] }
function buildTree(components) {
if (components.length === 0) {
throw new Error('Can\'t parse: empty components');
} else if (components.length === 1) {
return { text: components[0], leaf: true };
} else {
return {
text: components[0],
items: [buildTree(components.slice(1))]
}
}
}
// 'a.b' -> { text: 'a', items: [{ text: 'b', leaf: true }] }
function parseString(str) {
return buildTree(str.split('.'));
}
// Merge nodes with a same text.
function mergeSame(left, right) {
if (left.text !== right.text) {
throw new Error('Can\'t merge: different text ' + left.text + ', ' + right.text);
}
// Same text
if (left.leaf && right.leaf) {
return left;
} else if (left.leaf && !right.leaf) {
return right;
} else if (!left.leat && right.leaf) {
return left;
} else {
var concat = left.items.concat(right.items);
return { text: left.text, items: merge(concat) };
}
}
// Merge multiple nodes.
function merge(items) {
var textToItem = {};
var keys = [];
for (var i = 0; i < items.length; i++) {
var text = items[i].text;
if (textToItem[text]) {
textToItem[text] = mergeSame(textToItem[text], items[i]);
} else {
textToItem[text] = items[i];
keys.push(text);
}
}
keys.sort();
var merged = [];
for (i = 0; i < keys.length; i++) {
merged.push(textToItem[keys[i]]);
}
return merged;
}
function parse(strs) {
var nodes = [];
for (var i = 0; i < strs.length; i++) {
nodes.push(parseString(strs[i]));
}
return { items: merge(nodes) };
}
console.log(parseString('a.b.c.d'));
console.log(parse(["a.b.c.d","a.c.e.f","a.b.c.g"]));
It may look messy. I was not sure about your environment and didn't use map or reduce.

You can use this demo.
Just remove unnecessary formatting ;) and change to local variables as you want.
I have written just parsing logic for you.
function parceArrayToExtJSTreeNodes(arr) {
for(i in arr) {
addNodesToTree(arr[i].split('.'));
}
createExtJSString(root, 0, true);
}
http://jsfiddle.net/HxFL6/

Related

Getting File Directories as nested Array object

I want to get my file directories as nested array object but i can't seem to figure out how to convert
[
'routes/files',
'routes/files/emptyfolder',
'routes/files/somefolder',
'routes/files/somefolder/example.docx',
'routes/files/test.docx',
'routes/randomdata.json'
]
to
[
{
title: 'routes',
content: [
{
title: 'files',
content: [
{
title: 'empty folder',
content: []
},
{
title: 'somefolder',
content: [
{
title: 'example.docx',
},
]
},
{
title: 'test.docx',
}
],
},
{
title: 'randomdata.json'
}
],
}
]
it looks impossible problem for me to solve.
I would love to know how to solve it.
Thank you.
Here is how I solved it:
Not the best solution, but works.
const arr = [
"routes/files",
"routes/files/emptyfolder",
"routes/files/somefolder",
"routes/files/somefolder/example.docx",
"routes/files/test.docx",
"routes/randomdata.json",
];
const arr2 = arr.map((p) => p.split("/"));
const setNestedObjectField = (
obj,
props,
value
) => {
if (!Array.isArray(obj)) {
if (!obj.content) {
obj.content = [];
}
obj = obj.content;
}
for (const propName of props) {
const next = obj.find((el) => el.title === propName);
if (!next) {
console.assert(props.at(-1) === propName);
// last propName
obj.push(value);
} else {
if (!next.content) {
next.content = [];
}
obj = next.content;
}
}
};
const rez = [];
let index = 0;
while (arr2.some((s) => s[index] !== undefined)) {
// arr2 = arr2.filter((a) => a.length);
const layer = arr2.reduce((acc, pathArr) => {
if (pathArr[index] === undefined) return acc;
acc.add(pathArr.slice(0, index + 1).join("/"));
return acc;
}, new Set());
// console.log({ layer });
for (const key of layer) {
setNestedObjectField(rez, key.split("/"), { title: key.split("/").at(-1) });
}
index++;
}
console.log(rez);
I came across this question and it's an interesting problem, I know it's already been answered, but I wanted to spend a little of my time to solve it my way.
here I leave my code:
function nestedDirectories (arr) {
const splittedArray = arr.map(a => a.split('/'));
return {
mergeObjs: function(target, source) {
for (let key in source) {
if(!target[key]) target[key] = {};
target[key] = this.mergeObjs(target[key], source[key]);
}
return target;
},
buildResponse: function (objMain) {
let arr = [];
for (let key in objMain) {
let o = { title: key, content: [] };
if(key.includes(".")) {
delete o.content;
} else if (Object.keys(objMain[key]).length) {
o.content = this.buildResponse(objMain[key]);
}
arr.push(o);
}
return arr;
},
exec: function () {
let targetObject = {};
splittedArray.forEach(arrParent => {
let strObj = '';
for (let i = arrParent.length - 1; i >= 0 ; i--) {
strObj = `"${arrParent[i]}": {${strObj}}`;
}
let parseObj = JSON.parse(`{${strObj}}`);
targetObject = this.mergeObjs(targetObject, parseObj);
});
return this.buildResponse(targetObject);
}
}
}
and use it like this:
const dirs = [
'routes/files',
'routes/files/emptyfolder',
'routes/files/somefolder',
'routes/files/test.docx',
'routes/randomdata.json',
'routes/files/somefolder/example.docx'
];
const data = nestedDirectories(dirs).exec();
result:
[
{
title: 'routes',
content: [
{
title: 'files',
content: [
{ title: 'emptyfolder', content: [] },
{
title: 'somefolder',
content: [ { title: 'example.docx' } ]
},
{ title: 'test.docx' }
]
},
{ title: 'randomdata.json' }
]
}
]

How to clear the vue warn when I use vue-slider-component?

The issue detail:
1. I implement the feature with the vue-slider-component module, but that has a lot of warnings when I move the dots on the slider.
2. I know that the reason is that I used v-for to point to an object that will change, but I do not know how to fix this issue.
the following link is my test site:
https://jsfiddle.net/ncwv84x9/
enter image description here
My codes:
code1 (Html)
<div id="app">
<div class="box" v-for="(item,index) in columnvalue">
<label>{{item.text}}</label>
<input v-model="value[index]" />
</div>
<hr />
<br />
<vue-slider v-model="value" :order="false" :tooltip="'always'" :process="false" :marks="marks" :width="600">
<template slot="tooltip" slot-scope="{index}">
<div>{{getText(index)}}</div>
</template>
</vue-slider>
</div>
JavaScript + Vue:
new Vue({
el: '#app',
components: {
VueSlider: window['vue-slider-component']
},
data: function() {
return {
// collect the all values
columnvalue: [],
// stored disease value
pet_name: [{
text: 'dog',
index: 0
},
{
text: 'cat',
index: 1
}
],
// stored drug value
feeder_name: [{
text: 'Sam',
index: 0
}],
// from age filter
age: [
65, 100
],
test: "",
value: [],
process: dotsPos => [
[dotsPos[0], dotsPos[1], {
backgroundColor: 'pink'
}],
[dotsPos[1], dotsPos[2], {
backgroundColor: 'blue'
}],
[dotsPos[2], dotsPos[3], {
backgroundColor: 'black'
}],
],
after: {},
relations: [],
marks: {
'0': {
label: 'start',
margin: '0 0 0 10px'
},
'100': {
label: 'end',
labelStyle: {
left: '100%',
margin: '0 0 0 10px',
top: '50%',
transform: 'translateY(-50%)'
}
}
}
}
},
created: function() {
//vue instance 被 constructor 建立後,在這裡完成 data binding
let tmpArray = this.pet_name.concat(this.feeder_name);
let tmpValueArray = [];
for (i = 0; i < tmpArray.length; i++) {
tmpArray[i].index = i;
tmpValueArray.push(0);
}
this.columnvalue = tmpArray;
this.value = tmpValueArray;
},
methods: {
getText(index) {
const ani = this.columnvalue.find((v) => v.index == index).text;
this.after = {
...this.after,
[ani]: this.value[index]
}
return ani;
},
getNodeRelation() {
const indexs = this.after;
let result = [];
let result2 = [];
let placement = [];
for (obj in indexs) {
placement.push([obj, indexs[obj]]);
}
placement.sort(function(a, b) {
/* console.log(a[1]) */
return a[1] - b[1];
})
for (i = 0; i < placement.length; i++) {
if (i + 1 >= placement.length) {
break;
}
let distance = placement[i + 1][1] - placement[i][1];
let predicate = "";
if (distance > 0) {
predicate = "after";
} else if (distance == 0 && placement[i + 1][1] == 0) {
predicate = "hasUse";
} else {
predicate = "same";
}
let source = {
label: placement[i][0],
index: i
};
let target = {
label: placement[i + 1][0],
index: i
};
// store the 4-tuple reprsentations about slider
result2.push({
source: source,
target: target,
type: predicate,
days: distance
});
}
/* this.relations = "{\"relation\":" + JSON.stringify(result2)+"}" */
;
this.relations = JSON.stringify(result2);
},
getAllFilters() {
let vm = this;
let beginHas = true;
if (vm.relations.length == 0) {
vm.getNodeRelation();
beginHas = false;
}
let result = {
age: vm.age,
disease_name: vm.disease_name,
drug_name: vm.drug_name,
relation: vm.relations
};
if (!beginHas) {
vm.relations = [];
}
this.test = JSON.stringify(result);
}
},
})
I get a infinite loop error which disappears when I remove this section in getText()
this.after = {
...this.after,
[ani]: this.value[index]
}
This is because there is some reactivity triggered and re-renders the dom, which calls that function, which renders the dom and so on...

Need parent object from the provided array

I have the below object and if I send some array I need the parents of those object.
Lets say if I send {Id:113} , It should return [11,1]
[
{
Id:1,
Name:"Test",
children:[
{
Id:11,
Name:"Test",
children:[
{
Id:113,
Name:"Test",
children:[
]
},
{
Id:114,
Name:"Test",
children:[
]
}
]
},
{
Id:12,
Name:"Test",
children:[
]
},
{
Id:13,
Name:"Test",
children:[
{
Id:115,
Name:"Test",
children:[
{
Id:1111,
Name:"Test",
children:[
]
}
]
}
]
},
{
Id:14,
Name:"Test",
children:[
]
}
]
} {
Id:2,
Name:"Test",
children:[
]
}
]
I am able to fetch each children item by using the below code.
this.selectedOMStructure = function (structure, id) {
_.each(structure, function (_item) {
if (_item.Id == id) {
_item.isChecked = !_item.isChecked;
if (_item.children) {
_this.checkAllChildren(_item.children, _item.isChecked);
} else {
return;
}
} else if (_item.children) {
_this.selectedOMStructure(_item.children, id);
}
})
}
this.checkAllChildren = function (structure, value) {
_.each(structure, function (_item) {
_item.isChecked = value;
if (_item.children) {
_this.checkAllChildren(_item.children, value);
} else {
return;
}
})
}
But I have no idea how to find the parents of mentioned ID
I am not sure if this is a better approach. Try this,
data will be having your array. main() is the entry point. And the value will be stored in a.
var a = [];
function main() {
a = [];
for (let i = 0; i < data.length; i++) {
checkChildren(data[i], 1111);
}
console.log(a);
}
function checkChildren(node, id) {
if (node.Id == id) {
return id;
}
else if (node.children) {
for (let i = 0; i < node.children.length; i++) {
let r = checkChildren(node.children[i], id);
if (r != 0) {
a.push(node.Id);
return node.Id;
}
}
return 0;
}
}
main();
You will need to map the values from this data structure into a new data structure.
Consider using a recursive algorithm that attaches a new parent field to every item while also adding it to a map from id to the item.

Array of objects, combine values where key matches. with Underscore

Given the following array:
[
{
"real":104.1530776708426,
"workHour":8,
"value":null
},
{
"real":71.53948769310401,
"workHour":9
},
{
"real":97.84076993321577,
"workHour":10
},
{
"real":115.72564185649178,
"workHour":11
},
{
"real":79.95589800993977,
"workHour":12
},
{
"real":91.52846219558896,
"workHour":13
},
{
"real":57.86282092824589,
"workHour":14
},
{
"real":148.33923183423036,
"workHour":15
},
{
"real":125.19410346293202,
"workHour":16
},
{
"real":67.33128253468612,
"workHour":17
},
{
"real":55.75871834903695,
"workHour":18
},
{
"real":102.04897509163365,
"workHour":19
},
{
"real":132.55846249016332,
"workHour":20
},
{
"real":138.87077022779013,
"workHour":21
},
{
"real":60,
"workHour":8
},
{
"real":52,
"workHour":9
},
{
"real":114,
"workHour":10
},
{
"real":115,
"workHour":11
},
{
"real":92,
"workHour":12
},
{
"real":102,
"workHour":13
},
{
"real":54,
"workHour":14
},
{
"real":62,
"workHour":15
},
{
"real":133,
"workHour":16
},
{
"real":116,
"workHour":17
},
{
"real":106,
"workHour":18
},
{
"real":115,
"workHour":19
},
{
"real":115,
"workHour":20
},
{
"real":125,
"workHour":21
}
]
How can I find where the workHour match, and combine real there?
I did it with pure JS
const array = [{"real":104.1530776708426,"workHour":8,"value":null},{"real":71.53948769310401,"workHour":9},{"real":97.84076993321577,"workHour":10},{"real":115.72564185649178,"workHour":11},{"real":79.95589800993977,"workHour":12},{"real":91.52846219558896,"workHour":13},{"real":57.86282092824589,"workHour":14},{"real":148.33923183423036,"workHour":15},{"real":125.19410346293202,"workHour":16},{"real":67.33128253468612,"workHour":17},{"real":55.75871834903695,"workHour":18},{"real":102.04897509163365,"workHour":19},{"real":132.55846249016332,"workHour":20},{"real":138.87077022779013,"workHour":21},{"real":60,"workHour":8},{"real":52,"workHour":9},{"real":114,"workHour":10},{"real":115,"workHour":11},{"real":92,"workHour":12},{"real":102,"workHour":13},{"real":54,"workHour":14},{"real":62,"workHour":15},{"real":133,"workHour":16},{"real":116,"workHour":17},{"real":106,"workHour":18},{"real":115,"workHour":19},{"real":115,"workHour":20},{"real":125,"workHour":21}];
var map = {};
for (var i=0; i<array.length; i++) {
var obj = array[i],
id = obj.workHour;
if (id in map) { // we know this id already
// get the object and sum properties
map[id].real += obj.real;
} else // create a new one
map[id] = {
workHour: id,
real: obj.real,
};
}
console.log(map)
How can I do it with ES6? or Underscore?
Loop the array with Array#reduce to create a new object with combined values:
var data = [{"real":104.1530776708426,"workHour":8,"value":null},{"real":71.53948769310401,"workHour":9},{"real":97.84076993321577,"workHour":10},{"real":115.72564185649178,"workHour":11},{"real":79.95589800993977,"workHour":12},{"real":91.52846219558896,"workHour":13},{"real":57.86282092824589,"workHour":14},{"real":148.33923183423036,"workHour":15},{"real":125.19410346293202,"workHour":16},{"real":67.33128253468612,"workHour":17},{"real":55.75871834903695,"workHour":18},{"real":102.04897509163365,"workHour":19},{"real":132.55846249016332,"workHour":20},{"real":138.87077022779013,"workHour":21},{"real":60,"workHour":8},{"real":52,"workHour":9},{"real":114,"workHour":10},{"real":115,"workHour":11},{"real":92,"workHour":12},{"real":102,"workHour":13},{"real":54,"workHour":14},{"real":62,"workHour":15},{"real":133,"workHour":16},{"real":116,"workHour":17},{"real":106,"workHour":18},{"real":115,"workHour":19},{"real":115,"workHour":20},{"real":125,"workHour":21}];
var result = data.reduce(function(r, o) {
if (r[o.workHour]) {
r[o.workHour].real += o.real
} else {
r[o.workHour] = {
workHour: o.workHour,
real: o.real
}
}
return r;
}, {});
console.log(result);
Using lodash(/underscore), this uses _.reduce() with _.clone() to build an aggregate object. The values of this object are then output as an array of objects similar to your input array using _.values().
var data = [{"real":104.1530776708426,"workHour":8,"value":null},{"real":71.53948769310401,"workHour":9},{"real":97.84076993321577,"workHour":10},{"real":115.72564185649178,"workHour":11},{"real":79.95589800993977,"workHour":12},{"real":91.52846219558896,"workHour":13},{"real":57.86282092824589,"workHour":14},{"real":148.33923183423036,"workHour":15},{"real":125.19410346293202,"workHour":16},{"real":67.33128253468612,"workHour":17},{"real":55.75871834903695,"workHour":18},{"real":102.04897509163365,"workHour":19},{"real":132.55846249016332,"workHour":20},{"real":138.87077022779013,"workHour":21},{"real":60,"workHour":8},{"real":52,"workHour":9},{"real":114,"workHour":10},{"real":115,"workHour":11},{"real":92,"workHour":12},{"real":102,"workHour":13},{"real":54,"workHour":14},{"real":62,"workHour":15},{"real":133,"workHour":16},{"real":116,"workHour":17},{"real":106,"workHour":18},{"real":115,"workHour":19},{"real":115,"workHour":20},{"real":125,"workHour":21}];
var result = _.values(_.reduce(data, (sumObj, curr) => {
if (sumObj[curr.workHour])
sumObj[curr.workHour].real += curr.real;
else
sumObj[curr.workHour] = _.clone(curr);
return sumObj;
}, {}));
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Get string representation of JavaScript object

I have a object like so:
$scope.query = {
filter: {
column: {
productName: 'Some Product',
price: 29.95,
...
},
table: {
productType: 'GM',
categoryId: 1,
...
}
}
};
How do I get a string that represents the whole object in dot notation? e.g.
query.filter.table.productType
To clarify, I am using this string value as a key to store a key/value pair in localStorage.
I am using angular to $wacth each property on the object for a change. Since you can't watch an object and know which property changed with watching all, I need to get creative and store each property in a key/value pair.
You can do it recursively, and produces "key" in an array.
var obj = {
query: {
filter: {
table: {
productType: 'GM'
}
}
}
};
var stringify = function (e) {
var rs = [];
for (var k in e) {
if (e.hasOwnProperty(k)) {
if (typeof e[k] == 'object') {
var l = stringify(e[k]);
for (var i = 0; i < l.length; i++) {
rs.push(k + '.' + l[i]);
}
} else {
rs.push(k);
}
}
}
return rs;
}
console.log(stringify(obj));
outputs:
["query.filter.table.productType"]
fiddle
Demo
Before Ques Edit
var $scope = {
query: {
filter: {
table: {
productType: 'GM'
}
}
}
};
var k = JSON.stringify($scope)
//output "{"query":{"filter":{"table":{"productType":"GM"}}}}"
k.match(/\w+(?=\"\:)/g).join('.')
//output"query.filter.table.productType"
Edit
Updated Demo
If OP has no issue with the position of child elements
var $scope = {}
$scope.query = {
filter: {
column: {
productName: 'Some Product',
price: 29.95
},
table: {
productType: 'GM',
categoryId: 1,
}
}
};
k=JSON.stringify($scope)
{"query":{"filter":{"column":{"productName":"Some Product","price":29.95},"table":{"productType":"GM","categoryId":1}}}}
k.match(/\w+(?=\"\:)/g).join('.')
"query.filter.column.productName.price.table.productType.categoryId"
By iterating the properties into an array recursively you could create a hierarchical structure that represents the data in the object. From here you could parse the results out as you wish.
var scope = {
query: {
filter: {
column: {
productName: 'Some Product',
price: 29.95
},
table: {
productType: 'GM',
categoryId: 1
}
}
}
};
function buildProps(subject) {
var result = [];
for (var key in subject) {
if (subject.hasOwnProperty(key)) {
if (typeof subject[key] == "object") {
result.push(key, buildProps(subject[key]));
} else {
result.push(key);
}
}
}
return result;
}
function stringify(input) {
var result = [];
for (var i = 0; i < input.length; i++) {
if (typeof input[i] == "string") {
result.push(input[i]);
} else {
result = result.concat(stringify(input[i]));
}
}
return result.join('.');
}
console.log(buildProps(scope));
console.log(stringify(buildProps(scope)));
Parse out the strings in the resulting array/sub-arrays, format it any way you like.
In my simple example I just list them in order:
query.filter.column.productName.price.table.productType.categoryId

Categories