Converting js array into dictionary map - javascript

I have this array:
["userconfig", "general", "name"]
and I would like it to look like this
data_structure["userconfig"]["general"]["name"]
I have tried this function:
inputID = "userconfig-general-name"
function GetDataByID(inputID){
var position = '';
for (var i = 0; i < inputID.length; i++) {
var hirarchy = inputID[i].split('-');
for (var index = 0; index < hirarchy.length; index++) {
position += '["'+ hirarchy[index] +'"]';
}
}
return data_structure[position];
}
while hirarchy is the array. I get the [position] as a string which is not working well.
how can I make a js function which builds the object path dynamically by an array?

var arr = ["userconfig", "general", "name"];
var dataStructure = arr.reduceRight(function (value, key) {
var obj = {};
obj[key] = value;
return obj;
}, 'myVal');
Ends up as:
{ userconfig : { general : { name : 'myVal' } } }
Note that you may need a polyfill for the reduceRight method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight

The below function will take an object to modify and an array filled with the properties needed:
function objPath(obj,path){
path.forEach(function(item){
obj[item] = {};
obj = obj[item];
});
}
var myobj = {};
objPath(myobj,["test","test2","test3"]);
console.log(myobj);
//outputs
Object {test: Object}
test: Object
test2: Object
test3: Object
The function loops over the array creating the new object property as a new object. It then puts a reference to the new object into obj so that the next property on the new object can be made.
JSFiddle

Recursive function
var array = ["userconfig", "general", "name"];
function toAssociative(array) {
var index = array.shift();
var next = null;
if (array.length > 0) {
next = toAssociative(array);
}
var result = new Array();
result[index] = next;
return result;
}

Related

Dynamically creating objects with array of paths in dot notation

I was wondering if it is possible to dynamically generate an object with an array of strings in dot notation. I would like to dynamically build a JSON object from a CSV file. The goal is to build the CSV as JSON, then filter the properties and make a new JSON object.
So I would like to pass in something like this..
var obj = {};
var keyArray = ['meta', 'logos', 'warranty', 'specs', 'specs.engine', 'specs.engine.hp', 'specs.engine.rpm', 'specs.engine.manufacturer'];
The end result would be something like this...
obj = {
meta: {
},
logos: {
},
specs: {
engine: {
hp: {
}
}
}
}
Here is the main function
function addObjectsByKey(obj, keyArray) {
for (var key in keyArray) {
// If keyword is not in object notation
if (!(keyArray[key].match(/\./))) {
// If the object property is not set, set it
if (!(obj[keyArray[key]])) {
obj[keyArray[key]] = {};
}
} else {
// Split array element (in dot notation) into an array of strings
// These strings will be object properties
var pathAsArray = keyArray[key].split('.');
var path = null;
for (var k in pathAsArray) {
if (path == null) {
obj[pathAsArray[k]] = {};
path = pathAsArray[k];
} else {
obj[path][pathAsArray[k]] = {};
path += '.' + pathAsArray[k];
}
}
// throw Error('end');
}
}
// return obj;
}
You can use forEach loop and inside you can split each element on . and then use reduce method to build nested object.
var keyArray = ['meta', 'logos', 'warranty', 'specs', 'specs.engine', 'specs.engine.hp', 'specs.engine.rpm', 'specs.engine.manufacturer'];
const result = {}
keyArray.forEach(key => {
// Loop array of keys
// Split each key with . and use reduce on that
// In each iteration of reduce return r[e] which is going to be value if property exists
// or new object if it doesn't
// This way you can go to any object depth as long as keys match existing keys in object.
key.split('.').reduce((r, e) => r[e] = (r[e] || {}), result)
})
console.log(result)
Here is another approach using for loops that will return the same result.
var keyArray = ['meta', 'logos', 'warranty', 'specs', 'specs.engine', 'specs.engine.hp', 'specs.engine.rpm', 'specs.engine.manufacturer' ];
const result = {}
for(var i = 0; i < keyArray.length; i++) {
const keys = keyArray[i].split('.');
let ref = result;
for(var j = 0; j < keys.length; j++) {
const key = keys[j];
if(!ref[key]) ref[key] = {}
ref = ref[key]
}
}
console.log(result)
You can use the function reduce along with a nested forEach to build the path.
The first reduce will accumulate the nested operation.
The nested forEach will build the object and its children according to the current path separated by dots.
let keyArray = ['meta', 'logos', 'warranty', 'specs', 'specs.engine', 'specs.engine.hp', 'specs.engine.rpm', 'specs.engine.manufacturer'],
newObj = keyArray.reduce((accum, path) => {
let previous = accum;
path.split('.').forEach(key => {
if (previous[key]) previous = previous[key];
else previous = previous[key] = {};
});
return accum;
}, {});
console.log(newObj);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sum of same object name in javascript

Hi friends I'm beginner for javascript how i sum same n no's of object name corresponding value and push the result to new array.see this is sample object
var obj_1 ={'delivered':10,'due':11,'team_name':'UK'};
var obj_2 ={'delivered':10,'due':11,'team_name':'US'};
var obj_nth ={'delivered':10,'due':11,'team_name':'UK'};
but i expect this output [UK:{'delivered':20,'due':22},US:{'delivered':10,'due':11}],so please help me what i'll do next
You can first create array of objects and then reduce() to return one object.
var obj_1 ={'delivered':10,'due':11,'team_name':'UK'};
var obj_2 ={'delivered':10,'due':11,'team_name':'US'};
var obj_nth ={'delivered':10,'due':11,'team_name':'UK'};
var result = [obj_1, obj_2, obj_nth].reduce(function(r, e) {
if(!r[e.team_name]) {
r[e.team_name] = {delivered:0,due:0}
}
r[e.team_name].delivered += e.delivered
r[e.team_name].due += e.due
return r
}, {})
console.log(result)
const newArray = initialArray.map(({team_name, ...restProps}) => {
return {
[team_name]: {...restProps}
};
});
See:
Arrow functions
Spread operator
Array.prototype.map
Computed property names
var obj_1 ={'delivered':10,'due':11,'team_name':'UK'};
var obj_2 ={'delivered':10,'due':11,'team_name':'US'};
var obj_nth ={'delivered':10,'due':11,'team_name':'UK'};
function sum_all() {
var sum={};
for(var i=0;i<arguments.length;i++) {
obj = arguments[i];
if (!sum[obj.team_name]) {
sum[obj.team_name]={'delivered':0,'due':0};
}
sum[obj.team_name].delivered += obj.delivered;
sum[obj.team_name].due += obj.due;
}
return sum;
}
var sum = sum_all(obj_1,obj_2,obj_nth);
console.log(sum);
Your console output will be:
sum
Object
UK: Object
delivered: 20
due: 22
US: Object
delivered: 10
due: 11
Store these objects in an array, such as:
var myObjects = [
{'delivered':10,'due':11,'team_name':'UK'},
{'delivered':10,'due':11,'team_name':'US'},
{'delivered':10,'due':11,'team_name':'UK'}
];
Create a new object in which you will store your results:
var results = {};
Then iterate through the array with a for loop (as it is generally faster) and add the other properties according to team_name:
for (var i = 0; i <= myObjects.length; i++) {
if (typeof results[myObjects[i].team_name] !== undefined) {
results[myObjects[i]].delivered += myObjects[i].delivered;
results[myObjects[i]].due += myObjects[i].due;
} else {
// Set 0 to these properties if the entry didn't exist
results[myObjects[i]].delivered = 0;
results[myObjects[i]].due = 0;
}
}

How to restructure my JSON object

I implemented an aggregation function but the only problem I have now is that I lost my key: value format e.g [{name:"Apples",val:8},{name:"Banana",val: 9}].
function agrregate(a){
var targetObj = {};
var result;
var b = JSON.parse(JSON.stringify(a));
var trees= b.length;
if(!trees){
trees = 0
}
for (var i = 0; i < trees; i++) {
if (!targetObj.hasOwnProperty(b[i].key)) {
targetObj[b[i].key] = 0;
}
targetObj[b[i].key] += b[i].val;
}
result = JSON.stringify(targetObj);
return result;
}
This is the result i get when agrregate function completes.
{"Apple":8,"Banana":9}
Instead of
{name:"Apple", val:8}, {name:"Banana", val:9}
Use a reducer to aggregate. You don't need to do stuff with JSON stringify/parse.
To get back to an array of objects, you use map and Object.keys
var test = [{name:"Apples",val:5},{name:"Banana",val: 9},{name:"Apples",val:3}]
var aggregate = function(arr) {
return arr.reduce(function(result, obj) { // Create one object (result)
result[obj.name] = (result[obj.name] || 0) + obj.val; // Add a new key/or increase
return result // Return the object
}, {});
};
var wrap = function(obj) {
return Object.keys(obj) // Create an array of keys
.map(function(key) {
return { // Specify the format
name: key,
val: obj[key]
};
});
};
console.log(aggregate(test));
console.log(wrap(aggregate(test)));

merge two json arrays with push on item value

I have the following json array:
array 1:
fruits1 = [{"fruit":"banana","amount":"2","color":"yellow"},{"fruit":"apple","amount":"5","color":"red"},{"fruit":"kiwi","amount":"1","color":"green"}]
array 2:
fruits2 = [{"fruit":"banana","sold":"1","stock":"3"},{"fruit":"apple","sold":"3","stock":"5"},{"fruit":"kiwi","sold":"2","stock":"3"}]
I would like to get just one array which has the results merged according to the fruits value like this:
fruits = [{"fruit":"banana","amount":"2","color":"yellow","sold":"1","stock":"3"},{"fruit":"apple","amount":"5","color":"red","sold":"3","stock":"5"},{"fruit":"kiwi","amount":"1","color":"green","sold":"2","stock":"3"}]
I need to do something like
foreach item.fruit where fruit = fruit from initial array
fruits.push item
Any idea?
Try this logic:
function merge_options(obj1,obj2){
var obj3 = {};
for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
return obj3;
}
var obj1 = [];
for (var i = 0; i < fruits1.length ; i++) {
obj1[fruits1[i].fruit] = fruits1[i];
}
var obj2 = [];
for (var i = 0; i < fruits2.length ; i++) {
obj2[fruits2[i].fruit] = fruits2[i];
}
var fruits = []
for (var key in obj1) {
fruits.push(merge_options(obj1[key],obj2[key]));
}
console.log(fruits);
You can do something like this with javascript
// create a hash like {fruit_name -> object}
f1 = {};
fruits1.forEach(function(p) {
f1[p.fruit] = p;
});
// merge second array into above hash on fruit_name
fruits2.forEach(function(p) {
for (var a in p) { f1[p.fruit][a] = p[a];}
});
//fruits1 will now contain result;
//if you don't want to spoil fruit1 array, clone p inside 'fruits1.forEach' above before assigning it to 'f1[p.fruit]'. And at the end, create a new array out of f1
Here's a generic way that works with your data:
function joinObjects(initial, other, predicate, valueSelector) {
if(typeof(predicate) !== 'function') throw 'predicate must be a function';
if(typeof(valueSelector) !== 'function') throw 'valueSelector must be a function';
// make a clone of the original object so its not modified
var clone = jQuery.extend(true, {}, initial);
// iterate over the initial and other collections
for(var cloneKey in clone) {
if (!clone.hasOwnProperty(cloneKey)) continue;
for(var otherKey in other) {
if (!other.hasOwnProperty(otherKey)) continue;
// if the predicate is truthy, get the values
if (predicate(clone[cloneKey], other[otherKey])) {
// pull only the values you want to merge
var values = valueSelector(other[otherKey]);
// iterate over the values add them to the cloned initial object
for(var valueKey in values) {
if (values.hasOwnProperty(valueKey)) {
clone[cloneKey][valueKey] = values[valueKey];
}
}
}
}
}
return clone;
}
var fruits1 = [{"fruit":"banana","amount":"2","color":"yellow"},{"fruit":"apple","amount":"5","color":"red"},{"fruit":"kiwi","amount":"1","color":"green"}];
var fruits2 = [{"fruit":"banana","sold":"1","stock":"3"},{"fruit":"apple","sold":"3","stock":"5"},{"fruit":"kiwi","sold":"2","stock":"3"}];
var finalFruits = joinObjects(fruits1, fruits2,
function(left, right) { return left.fruit == right.fruit },
function(other) {
return {
sold: other.sold,
stock: other.stock
};
});
console.log(finalFruits);

Easy way to set javascript object multilevel property?

I am trying to create a javascript object like
var allUserExpiry={};
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
But I am getting an error like allUserExpiry[aData.userId] undefined.
Is there a way, whereby I can set multi-level JS-Object keys? or is it important that I should go by doing allUserExpiry[aData.userId]={}, then allUserExpiry[aData.userId][aData.courseId]={} ?
Please let me know if there are any utility functions available for the same.
No, there is no way to set "multilevel keys". You need to initialize each object before trying to add properties to it.
var allUserExpiry = {};
allUserExpiry[aData.userId] = {}
allUserExpiry[aData.userId][aData.courseId] = {}
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
Using Computed property names from ES6, it is possible to do:
var allUserExpiry = {
[aData.userId] = {
[aData.courseId]: {
[aData.uscId]: aData
}
}
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
Simply use loadash,
let object = {};
let property = "a.b.c";
let value = 1;
_.set(object, property, value); // sets property based on path
let value = _.get(object, property, default); // gets property based on path
Or you can do it:
function setByPath(obj, path, value) {
var parts = path.split('.');
var o = obj;
if (parts.length > 1) {
for (var i = 0; i < parts.length - 1; i++) {
if (!o[parts[i]])
o[parts[i]] = {};
o = o[parts[i]];
}
}
o[parts[parts.length - 1]] = value;
}
And use:
setByPath(obj, 'path.path2.path', someValue);
This approach has many weak places, but for fun... :)
Why not just do this?
var allUserExpiry={};
allUserExpiry[aData.userId] = {aData.courseId: {aData.uscId: aData}};
I have a pretty hacky but short way of doing it in IE9+ as well as real browsers.
Given var path = 'aaa.bbb.ccc.ddd.eee'; where path is what your intending to make into an object and var result = {}; will will create the object {aaa: {bbb: {ccc: {ddd: {eee: {}}}}}
result = {}
path.split('.').reduce(function(prev, e) {
var newObj = {};
prev[e] = newObj;
return newObj;
}, result);
will store the object in result.
How it works:
split('.') converts the input into ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
reduce(function (...) {...}, result) runs through the array created by split, and for each entry will pass along a returned value to the next one. In our case we pass the new object through after adding the new object to the old one. This creates a chain of objects. reduce returns the last object you return inside of it, so we have to defined result beforehand.
This relies on using references so it won't be immediately clear how it works if you're expecting your code to be maintained by anyone else and should probably be avoided to be honest, but it works at least.
You can also use the following to create the initial structure:
var x = function(obj, keys) {
if (!obj) return;
var i, t;
for (i = 0; i < keys.length; i++) {
if (!t) {
t = obj[keys[i]] = {};
} else {
t[keys[i]] = {};
t = t[keys[i]];
}
}
};
var a = {};
x(a, ['A', 'B', 'C', 'D', 'E', 'F']);
Another approach without strings or array as argument.
function fillObject() {
var o = arguments[0];
for(var i = 1; i < arguments.length-1; i++) {
if(!o.hasOwnProperty(arguments[i])) {
o[arguments[i]] = {};
}
if(i < arguments.length-2) {
o = o[arguments[i]];
}else {
o[arguments[i]] = arguments[i+1]
}
}
}
var myObj = {"foo":{}};
fillObject(myObj,"back","to","the","future",2);
console.log(JSON.stringify(myObj));
// {"foo":{},"back":{"to":{"the":{"future":2}}}}
But I wouldn't use it :-) It's just for fun.
Because I don't like too much intelligent algorithm. (If it was in this category)
Using lodash you can do this easily (node exists and empty check for that node)..
var lodash = require('lodash-contrib');
function invalidateRequest(obj, param) {
var valid = true;
param.forEach(function(val) {
if(!lodash.hasPath(obj, val)) {
valid = false;
} else {
if(lodash.getPath(obj, val) == null || lodash.getPath(obj, val) == undefined || lodash.getPath(obj, val) == '') {
valid = false;
}
}
});
return valid;
}
Usage:
leaveDetails = {
"startDay": 1414998000000,
"endDay": 1415084400000,
"test": { "test1" : 1234 }
};
var validate;
validate = invalidateRequest(leaveDetails, ['startDay', 'endDay', 'test.test1']);
it will return boolean.
Another solution using reduce function (thanks Brian K).
Here we created a get/set to general proposes. The first function return the value in any level. The key is splited considering the separator. the function return the value refered from last index in the key's array
The second function will set the new value considering the last index of the splited key
the code:
function getObjectMultiLevelValue(_array,key,separator){
key = key.split(separator || '.');
var _value = JSON.parse(JSON.stringify(_array));
for(var ki in key){
_value = _value[key[ki]];
}
return _value;
}
function setObjectMultiLevelValue(_array,key,value,forcemode,separator){
key.split(separator || '.').reduce(function(prev, currKey, currIndex,keysArr) {
var newObj = {};
if(prev[currKey] && !forcemode){
newObj = prev[currKey];
}
if(keysArr[keysArr.length-1] == currKey){
newObj = value;
prev[currKey] = newObj;
}
prev[currKey] = newObj;
return newObj;
}, _array);
return _array;
}
//testing the function
//creating an array
var _someArray = {a:'a',b:'b',c:{c1:'c1',c2:{c21:'nothing here...'}}};
//a multilevel key to test
var _key = 'a,a1,a21';
//any value
var _value = 'new foo in a21 key forcing replace old path';
//here the new value will be inserted even if the path exists (forcemode=true). Using comma separator
setObjectMultiLevelValue(_someArray,_key,_value,true,',');
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//inserting another value in another key... using default separator
_key = 'c.c2.c21';
_value = 'new foo in c21 key';
setObjectMultiLevelValue(_someArray,_key,_value);
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//recovering the saved value with different separators
_key = 'a,a1,a21';
console.log(getObjectMultiLevelValue(_someArray,_key,','));
_key = 'c.c2.c21';
console.log(getObjectMultiLevelValue(_someArray,_key));
Let assume our object is
const data = {
//some other data
userInfo: {},
};
First, define a new property of that object
data.userInfo.vehicle = {};
then simply
data.userInfo.vehicle.vehicleType = state.userInfo.vehicleType;

Categories