JavaScript: transform a string to json with some rules - javascript

I need to write a function which can transform the following data (input string) to a json.
data:
AA,BB
,CC
,,DD
,EE
JSON:
{
"AA": [
{
"BB": []
},
{
"CC": [
{
"DD": []
}
]
},
{
"EE": []
}
]
}
AA, BB, CC ... can any string and the data is input as a string.
I write a part of the function (see the following codes) and have no more good idea.
function str2json(str) {
var lines = str.split("\n");
var arr = new Array();
for (let i = 0; i < lines.length; i++)
{
var objs = lines[i].split(",");
arr.push(objs);
}
for (let i=0; i < arr.length; i++)
{
for (let j=0; j < arr[i].length; j++)
{
if(arr[i][j].length === 0)
{
arr[i][j] = arr[i-1][j];
}
else
{
break;
}
}
}
// TODO
}
I just transform the data into an array then add right data into null, the result is:
AA,BB
AA,CC
AA,CC,EE
AA,FF
Then I thought of using a loop to create an object then use JSON.stringify(object).
How can I continue or redo it?
Thanks!

You could take a helper array for all last levels and pick the result from the first element.
This works because you take after splitting an empty string as flag for taking the last array for inserting a new object.
var data = 'AA,BB\n,CC\n,,DD\n,EE',
result = [],
levels = [result];
data
.split('\n')
.forEach(s => s
.split(',')
.forEach((v, i) => {
if (!v) return;
levels[i].push({ [v]: (levels[i + 1] = []) });
})
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Find combination of nested JSON keys

I am having some trouble solving this issue without using nested for-loops. I would like to do it using recursion.
Given the following object:
{
"Color": {
"Black": [],
"White": []
},
"Effect": {
"30W": [],
"40W": [],
"60W": []
}
}
The code should compute each combination of Color and Effect and add a number in the list such that the following is produced:
{
"Color": {
"Black": [
1,
2,
3
],
"White": [
4,
5,
6
]
},
"Effect": {
"30W": [
1,
4
],
"40W": [
2,
5
],
"60W": [
3,
6
]
}
}
My attempt is as follows:
const func = (object, entries) => {
for (let prop in object) {
let counter = 0;
const objLength = Object.keys(object[prop]).length;
for (let key in object[prop]) {
console.log(key + counter)
for (let i = 0; i < entries.length / objLength; i++) {
object[prop][key].push(entries[counter]);
counter++;
}
}
}
return object;
}
However, this does not return the desired output. I think it is because of the inner-most for loop condition.
The best way to handle this is to create your JavaScript object and convert it to a string.
// creating your object with attributes. Objects in objects or whatever you
// need
var obj = new Object();
obj.name = "Dale";
obj.age = 30;
obj.married = true;
dataToAdd.forEach(function(item, index) {
item.married = false;
})
// Then convert it to a string using the following code
var jsonString= JSON.stringify(obj);
console.log(jsonString);
I solved the question by considering the space of each attribute key. Then it is just a matter of finding the cartesian, and adding values accordingly:
const cartesian =(...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));
function diy(jsonObj, counter) {
let permObj = []
let keys = Object.keys(jsonObj)
keys.forEach(key => {
permObj.push(Object.keys(jsonObj[key]))
});
permObj = cartesian(...permObj)
for(let i = 0; i < keys.length; i++) {
for(let j = 0; j < permObj.length; j++) {
jsonObj[keys[i]][permObj[j][i]].push(j + counter);
}
}
return jsonObj;
}

Compare two arrays with objects and show which item is deleted

I have two arrays which I want to compare and check if there is an deleted item in one of these arrays. If there is show me the difference (deleted item)
Here is the code below how I would like to achieve this:
function compareFilters (a1, a2) {
var a = [], diff = [];
for (var i = 0; i < a1.length; i++) {
a[a1[i]] = true;
}
for (var i = 0; i < a2.length; i++) {
if (a[a2[i]]) {
delete a[a2[i]];
} else {
a[a2[i]] = true;
}
}
for (var k in a) {
console.log('k', k);
diff.push(k);
}
return diff;
}
console.log(filters);
console.log(filters, queryBuilderFilters, compareFilters(queryBuilderFilters, filters));
This will log both arrays which look like this:
[
0: {
id: "AggregatedFields.ReturnOnAdSpend",
label: "ROAS (Return on Ad Spend)",
type: "integer",
description: "The ROAS (Return on Ad Spend)."
},
1: {
id: "AggregatedFields.ROIExcludingAssisted",
label: "ROI excl. assisted",
type: "integer",
description: "The ROI excluding any assisted values.
}
]
And the output of the compareFilters function is 0: "[object Object]"
How can I return the label of the object in this function?
This example illustrates what you want
var completedList = [1,2,3,4,7,8];
var invalidList = new Set([3,4,5,6]);
// filter the items from the invalid list, out of the complete list
var validList = completedList.filter((item) => {
return !invalidList.has(item);
})
console.log(validList); // Print [1,2,7,8]
// get a Set of the distinct, valid items
var validItems = new Set(validList);
You can try this, supposing that the objects in each array have the same reference and are not copies:
function compareFilters (a1, a2) {
const a1l = a1.length;
const a2l = a2.length;
// Both arrays are considered to be equal
if(a1l === a2l) return;
let completed;
let unCompleted;
let deletedValue;
if(a1l > a2l) {
completed = a1;
unCompleted = a2;
} else {
completed = a2;
unCompleted = a1;
}
for(let i = 0; i < completed.lenth; i++) {
if(completed[i] !== unCompleted[i]) {
return completed[i].label;
}
}
}
It will return undefined in case both arrays has the same quantity of elements. Otherwise, it will return the label of first element that is in one array but not the another.

How to convert an array of paths into JSON structure?

I found the question How to convert a file path into treeview?, but I'm not sure how to get the desired result in JavaScript:
I'm trying to turn an array of paths into a JSON tree:
https://jsfiddle.net/tfkdagzv/16/
But my path is being overwritten.
I'm trying to take something like this:
[
'/org/openbmc/path1',
'/org/openbmc/path2',
...
]
... and turn it into...
output = {
org: {
openbmc: {
path1: {},
path2: {}
}
}
}
I'm sure this is pretty easy, but I'm missing something.
const data = [
"/org/openbmc/examples/path0/PythonObj",
"/org/openbmc/UserManager/Group",
"/org/openbmc/HostIpmi/1",
"/org/openbmc/HostServices",
"/org/openbmc/UserManager/Users",
"/org/openbmc/records/events",
"/org/openbmc/examples/path1/SDBusObj",
"/org/openbmc/UserManager/User",
"/org/openbmc/examples/path0/SDBusObj",
"/org/openbmc/examples/path1/PythonObj",
"/org/openbmc/UserManager/Groups",
"/org/openbmc/NetworkManager/Interface"
];
const output = {};
let current;
for (const path of data) {
current = output;
for (const segment of path.split('/')) {
if (segment !== '') {
if (!(segment in current)) {
current[segment] = {};
}
current = current[segment];
}
}
}
console.log(output);
Your solution was close, you just didn't reset the current variable properly. Use this:
current = output;
instead of this:
current = output[path[0]];
This function should do :
var parsePathArray = function() {
var parsed = {};
for(var i = 0; i < paths.length; i++) {
var position = parsed;
var split = paths[i].split('/');
for(var j = 0; j < split.length; j++) {
if(split[j] !== "") {
if(typeof position[split[j]] === 'undefined')
position[split[j]] = {};
position = position[split[j]];
}
}
}
return parsed;
}
Demo
var paths = [
"/org/openbmc/UserManager/Group",
"/org/stackExchange/StackOverflow",
"/org/stackExchange/StackOverflow/Meta",
"/org/stackExchange/Programmers",
"/org/stackExchange/Philosophy",
"/org/stackExchange/Religion/Christianity",
"/org/openbmc/records/events",
"/org/stackExchange/Religion/Hinduism",
"/org/openbmc/HostServices",
"/org/openbmc/UserManager/Users",
"/org/openbmc/records/transactions",
"/org/stackExchange/Religion/Islam",
"/org/openbmc/UserManager/Groups",
"/org/openbmc/NetworkManager/Interface"
];
var parsePathArray = function() {
var parsed = {};
for(var i = 0; i < paths.length; i++) {
var position = parsed;
var split = paths[i].split('/');
for(var j = 0; j < split.length; j++) {
if(split[j] !== "") {
if(typeof position[split[j]] === 'undefined')
position[split[j]] = {};
position = position[split[j]];
}
}
}
return parsed;
}
document.body.innerHTML = '<pre>' +
JSON.stringify(parsePathArray(), null, '\t')
'</pre>';
(see also this Fiddle)
NB: The resulting arrays need to be merged
This method works for both files & directories, and by using only arrays as the data format.
The structure is based upon arrays being folders, the first element being the folder name and the second - the contents array.
Files are just regular strings inside the array (but could easily be objects containing properties)
Converts =>
[
'/home/',
'/home/user/.bashrc',
'/var/',
'/var/test.conf',
'/var/www/',
'/var/www/index.html',
'/var/www/index2.html'
]
To =>
[
['home', [
['user', [
'.bashrc'
]]
]],
['var', [
'test.conf',
['www', [
'index.html',
'index2.html'
]]
]]
]
Script:
var paths = [
'/var/',
'/var/test.conf',
'/var/www/',
'/var/www/index.html',
'/var/www/index2.html'
]
var parsed = []
for (let path of paths) {
let tree = path.split('/')
let previous = parsed
console.groupCollapsed(path)
for (let item in tree) {
const name = tree[item]
const last = item == tree.length - 1
if (name) {
if (last) {
console.log('File:', name)
previous.push(name) - 1
} else {
console.log('Folder:', name)
let i = previous.push([name, []]) - 1
previous = previous[i][1]
}
}
}
console.groupEnd(path)
}
console.warn(JSON.stringify(parsed))

Join same properties value from Objects

I have an array of object, I want to know the best way of concatenating values from similar properties e.g.
arr:[
{obj:{obj_type:1, obj_foo:"joe"}},
{obj:{obj_type:2, obj_foo:"developer"}},
{obj:{obj_type:1, obj_foo:"kevin"}},
{obj:{obj_type:2, obj_foo:"architect"}}
]
I need to concatenate properties value of same obj_type property.
expected result should be:
arr:[
{obj:{obj_type:1, obj_foo:"joe|kevin"}},
{obj:{obj_type:2, obj_foo:"developer|architect"}}
]
i.e. values are concatenated based on obj_type.
I think code like this might be helpful for you:
//Objects to work with:
var arr = [{obj:{obj_type:1, obj_foo:"joe"}},
{obj:{obj_type:2, obj_foo:"developer"}},
{obj:{obj_type:1, obj_foo:"kevin"}},
{obj:{obj_type:2, obj_foo:"architect"}}];
//Map from obj_type to {obj: …} objects:
var map = {};
//Iterating arr:
for(var i = 0; i < arr.length; i++){
var o = arr[i], type = o.obj.obj_type;
if(type in map){
map[type].obj.obj_foo += '|' + o.obj.obj_foo;
}else{
map[type] = o;
}
}
//Putting map values to arr:
arr = [];
for(var key in map){
arr.push(map[key]);
}
//Done:
console.log(arr);
Produced output looks like this:
[ { obj: { obj_type: 1, obj_foo: 'joe|kevin' } },
{ obj: { obj_type: 2, obj_foo: 'developer|architect' } } ]
This variant doesn't change content of initial array.
var types = {};
var newArr = [];
var type, newObj;
for ( var i = 0; i < arr.length; ++i ) {
type = arr [ i ].obj.obj_type;
if ( type in types ) {
types[ type ].obj.obj_foo += '|' + arr[ i ].obj.obj_foo;
} else {
newObj = {
obj: {
obj_type: arr[ i ].obj.obj_type,
obj_foo: arr[ i ].obj.obj_foo
}
};
types[ type ] = newObj;
newArr.push( newObj );
}
}
return newArr; // result array
This might be the simplest approach:
// Your array
var arr = [
{obj:{obj_type:1, obj_foo:"joe"}},
{obj:{obj_type:2, obj_foo:"developer"}},
{obj:{obj_type:1, obj_foo:"kevin"}},
{obj:{obj_type:2, obj_foo:"architect"}}
];
// Loop over all elements
for(var i = 0; i < arr.length; i++) {
var a = arr[i].obj;
// Compare to each other element
for(var j = i + 1; j < arr.length; j++) {
var b = arr[j].obj;
// If the obj_type is equal...
if(a.obj_type === b.obj_type) {
// Merge data...
a.obj_foo += '|' + b.obj_foo;
// Remove other element
arr.splice(j--, 1);
}
}
}
Output (from node.js):
[ { obj: { obj_type: 1, obj_foo: 'joe|kevin' } },
{ obj: { obj_type: 2, obj_foo: 'developer|architect' } } ]

JavaScript looping through an array to check for numeric values

I have an an array that returns the values such as the following:
//Eg 1 [ [ "214323", "34243" ], [ "3333", "123" ] ]
//Eg 2 [ [ "214323" ],[ "3333" ] ]
I want to validate if the array holds only numbers with no spaces or null, else I would like to throw an error.
This is my following code and it does not work in the above example. It throws out an error even though all values are numeric.
for (var i = 0; i <= arrayVals.length; i++) {
if(!(/^\d+$/.test(arrayVals[i]))) {
err_comp=true;
}
}
if( err_comp ==true) {
alert( 'The value has to be only numeric.');
}
You have an array of arrays, thus you need two loops:
var err_comp = false;
for (var i = 0; i < arrayVals.length; i++) {
var arr = arrayVals[i];
for (var j = 0; j < arr.length; j++) {
if (!(/^\d+$/.test(arr[j]))) {
err_comp = true;
}
}
}
Otherwise, you'd be testing /^\d+$/.test([ "214323", "34243" ]).
you should not use <= because you start with 0 you should use <:
for (var i = 0; i < arrayVals.length;
multi_arr.every(function(arr) {
return arr.every(function(n) {
return /^\d+$/.test(n);
});
});
You can change the test as you need, and you can add a .every patch for IE8 if you want.
And you can make reusable your functions.
function forEveryArray(fn) {
return function every_array(arr) {
return arr.every(fn)
}
}
function isNumber(n) { return /^\d+$/.test(n); }
multi_arr.every(forEveryArray(isNumber));

Categories

Resources