JS - Lodash; Nested Objects (parents/children) to flat array - javascript

I am using lodash (although solution doesn't have to) and I want to convert the following structure:
{ prop1: 'root',
prop2: 'someVal',
children: [
{ prop1: 'first Child',
prop2: 'some other val',
children: [
{ prop1: 'last child'
prop2: 'another value'
children: []
}
]
}
]
}
to a flat array:
[ { prop1: 'root', prop2: 'someVal' },
{prop1: 'firstChild', prop2: 'some Other Val'},
{prop1: 'last child', prop2: 'another value'}
]
The depth can vary, and the last child will always have [] assigned to its children property; Note that in that particular case, the children array will always have a single item in it
Should be fairly straightforward but it seems I just can't put the finger on it for some reasons
Thanks

Solved with this snippet (in CoffeeScript)
flatten = (node) ->
row = node
_.each node.children or [], (el) ->
flatten el
ancestors.push row

This is for using an anonymous target to flatten into (e.g. flattened)
flatten = (collection, flattened = [])->
_.each collection, (obj)->
flattened.push obj
flatten obj.children, flattened
flattened

Solution using pure JS:-
http://jsbin.com/bazilurolu/edit?js,console
var flatArray = function(input){
var result = [];
(function(obj){
var arr = [], newObj = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if(key !== "children"){
newObj[key] = obj[key];
}
else{
arr = obj[key];
}
}
}
result.push(newObj);
if(arr.length){
arguments.callee(arr[0]);
}
})(input);
return result;
};

Here is an Typescript version of the solution of #jusopi
const flatten = (collection: any[], flattened?: any[]) => {
if (!flattened) {
flattened = [];
}
_.each(collection, (obj) => {
flattened!.push(obj);
flatten(obj.children, flattened);
});
return flattened;
};

Related

Javascript - denormalize JSON array using map function instead of nested for loop

I'm trying to get my head around map functions.
Here is my working code and output using a nested for loop:
var jsonsToAddTo = [
{'cat':'k1','key2':'a'},
{'cat':'k1','key2':'b'},
{'cat':'k2','key2':'a'},
{'cat':'k2','key2':'b'},
{'cat':'k3','key2':'a'}
]
var additionalData = [
{'pk':'k1','key3':'data1'},
{'pk':'k2','key3':'data2'},
{'pk':'k3','key3':'data3'},
]
// Adds a key value pair from sourceJson to targetJson based on a matching value
function denormalizeJsonOnKey(targetJsonArray,targetKeyToMatch, sourceJsonArray, sourceKeyToMatch, keyToAdd){
for(thisJson in targetJsonArray){
for(thatJson in sourceJsonArray){
if(targetJsonArray[thisJson][targetKeyToMatch]==sourceJsonArray[thatJson][sourceKeyToMatch]){
console.log('match');
targetJsonArray[thisJson][keyToAdd]=sourceJsonArray[thatJson][keyToAdd];
}
}
}
return targetJsonArray
}
console.log(denormalizeJsonOnKey(jsonsToAddTo,'cat',additionalData,'pk','key3'))
OUTPUT:
[
{ cat: 'k1', key2: 'a', key3: 'data1' },
{ cat: 'k1', key2: 'b', key3: 'data1' },
{ cat: 'k2', key2: 'a', key3: 'data2' },
{ cat: 'k2', key2: 'b', key3: 'data2' },
{ cat: 'k3', key2: 'a', key3: 'data3' }
]
I can't figure out how to handle the nesting using a map function on an array.
Using ES6 can simplify using Array#find() and Object#assign()
var data = [
{'cat':'k1','key2':'a'},
{'cat':'k1','key2':'b'},
{'cat':'k2','key2':'a'},
{'cat':'k2','key2':'b'},
{'cat':'k3','key2':'a'}
]
var data2 = [
{'pk':'k1','key3':'data1'},
{'pk':'k2','key3':'data2'},
{'pk':'k3','key3':'data3'},
]
const mergeData= (arr1, arr2, matchKey, filterKey, includeKey)=>{
arr1.forEach(o => {
const newObj ={};
const match = arr2.find(e => e[filterKey] === o[matchKey])
newObj[includeKey] = match ? match[includeKey] : null;
Object.assign(o, newObj);
});
}
mergeData(data, data2,'cat', 'pk', 'key3')
console.log(data)
Here is a solution that takes advantage of map and object spread to produce a new array with the desired key added into the target array's elements:
var jsonsToAddTo = [
{'cat':'k1','key2':'a'},
{'cat':'k1','key2':'b'},
{'cat':'k2','key2':'a'},
{'cat':'k2','key2':'b'},
{'cat':'k3','key2':'a'}
]
var additionalData = [
{'pk':'k1','key3':'data1'},
{'pk':'k2','key3':'data2'},
{'pk':'k3','key3':'data3'},
]
function denormalizeJsonOnKey(targetJsonArray,targetKeyToMatch, sourceJsonArray, sourceKeyToMatch, keyToAdd){
return targetJsonArray.map(thisJson => {
const addObj = sourceJsonArray.find(thatJson => thatJson[sourceKeyToMatch] === thisJson[targetKeyToMatch]);
return {
...thisJson,
...addObj ? {[keyToAdd]: addObj[keyToAdd]} : {},
}
});
}
console.log(denormalizeJsonOnKey(jsonsToAddTo, 'cat', additionalData, 'pk', 'key3'))
Note that this solution won't mutate the original array, so the jsonsToAddTo variable will be the same after you invoke the function. If you want to replace the original, you can always just re-assign it:
jsonsToAddTo = denormalizeJsonOnKey(jsonsToAddTo, 'cat', additionalData, 'pk', 'key3')
Try this,
using maps for both iteration,
var jsonsToAddTo = [{'cat':'k1','key2':'a'},{'cat':'k1','key2':'b'},
{'cat':'k2','key2':'a'},{'cat':'k2','key2':'b'},
{'cat':'k3','key2':'a'}]
var additionalData = [{'pk':'k1','key3':'data1'},{'pk':'k2','key3':'data2'},{'pk':'k3','key3':'data3'},
]
function denormalizeJsonOnKey(targetJsonArray,targetKeyToMatch, sourceJsonArray, sourceKeyToMatch, keyToAdd){
jsonsToAddTo.map((obj,index)=> {
additionalData.map((o,idx)=> {
if(obj[targetKeyToMatch]==o[sourceKeyToMatch]){
obj[keyToAdd]=o[keyToAdd];
}
})
})
return jsonsToAddTo
}
console.log(denormalizeJsonOnKey(jsonsToAddTo,'cat',additionalData,'pk','key3'))
var targetJsonArray = jsonsToAddTo.map(function(json, index) {
additionalData.forEach(function(data) {
if (data.pk === json.cat) {
json.key3 = data.key3;
}
})
return json;
})
Rather than nesting loops here, which will iterate the entire additionalData array for every entry in jsonsToAddTo, I suggest building an object map of the additionalData dataset once at the beginning, and then reference this within a .map on the target dataset:
var jsonsToAddTo = [
{'cat':'k1','key2':'a'},
{'cat':'k1','key2':'b'},
{'cat':'k2','key2':'a'},
{'cat':'k2','key2':'b'},
{'cat':'k3','key2':'a'}
]
var additionalData = [
{'pk':'k1','key3':'data1'},
{'pk':'k2','key3':'data2'},
{'pk':'k3','key3':'data3'},
]
// Adds a key value pair from sourceJson to targetJson based on a matching value
function denormalizeJsonOnKey(targetJsonArray,targetKeyToMatch, sourceJsonArray, sourceKeyToMatch, keyToAdd){
// Build an object of items keyed on sourceKeyToMatch
const sourceJsonMap = sourceJsonArray.reduce((obj, item) => (obj[item[sourceKeyToMatch]]=item, obj), {});
return targetJsonArray.map(item => {
const targetValue = item[targetKeyToMatch];
if (sourceJsonMap.hasOwnProperty(targetValue)) {
item[keyToAdd] = sourceJsonMap[targetValue][keyToAdd];
}
return item;
});
}
console.log(denormalizeJsonOnKey(jsonsToAddTo,'cat',additionalData,'pk','key3'))
Doing it this way should be far more efficient, especially if the dataset you are working on is fairly large.

Convert Flat JSON Array to JSON Object in Nodejs

I have a flat json array that store data like this:
[
{
"prop1": "prop1Data1"
},
{
"prop2": "prop2Data1"
},
{
"prop3.name": "Tom"
}
]
How can I convert this data into simple json object in node js like this:
{ "prop1": "prop1Data1", "prop2": "prop2Data1", "prop3.name": "Tom" }
You could use Object.assign and use spread syntax ... for the array.
var array = [{ prop1: "prop1Data1" }, { prop2: "prop2Data1" }, { "prop3.name": "Tom" }],
object = Object.assign({}, ...array);
console.log(object);
ES5 with Array#reduce and by iterating the keys.
var array = [{ prop1: "prop1Data1" }, { prop2: "prop2Data1" }, { "prop3.name": "Tom" }],
object = array.reduce(function (r, o) {
Object.keys(o).forEach(function (k) {
r[k] = o[k];
});
return r;
}, {});
console.log(object);
The way that I've done it was like so, since it is within an array.
var original = [{"prop1": "prop1Data1"},{"prop2": "prop2Data1"},{"prop3.name": "Tom"}];
var propStore = {
prop1 : '',
prop2 : '',
prop3 : ''
}
propStore.prop1 = original[0]["prop1"];
propStore.prop2 = original[0]["prop2"];
propStore.prop3 = original[0]["prop3"];
console.log(propStore);

changing an array of javascript objects with a single key to an array that contains the objects' data

I currently have data in the following format:
var anArray = [
obj1: {
key1: data1
},
obj2: {
key2: data2
},
];
I would like the data to instead be in the following format:
var array2 = [data1, data2];
for some reason, I cannot figure out a concise way to to this. I know it could be done with a forEach loop that iterates over each object and pushes it onto a new array, but I would prefer to be more elegant (and shorter if possible) than that.
const anArray = {
obj1: {
key1: "A"
},
obj2: {
key2: "B"
},
};
const result = Object.keys(anArray).map(key => {
const obj = anArray[key];
return Object.keys(obj).map(key => obj[key])[0];
});
console.log(result);
Given that anArray is actually properly structured to be valid, then you could do the following:
Note that in this case anArray isn't an actual array but rather a object literal
var anArray = {
obj1: {
key1: "data1"
},
obj2: {
key2: "data2"
},
};
var array2 = []
for(i in anArray){
for(j in anArray[i]){
array2.push(anArray[i][j])
}
}
console.log(array2)
https://jsfiddle.net/wh4r0w5s/
Try with:
const arr1 = [
{key1:'value1'},
{key2:'value2'}
]
const res = arr1.map(obj => {
return Object.keys(obj).map(val => obj[val])
}).reduce((acc,v) => {
return acc.concat(v);
},[]);
console.log(res);
update
But if you have the following form:
var anArray = [
obj1: {
key1: data1
},
obj2: {
key2: data2
},
];
It's better to apply a recursive function, as follow:
const arr1 = [
{
obj1:{key1:'value1',key3:'value3'}
},
{
obj2:{key2:'value2'}
}
]
const getValuesFromObj = (obj) => {
if(typeof obj === 'string')
return obj;
return Object.keys(obj).map(key => {
return getValuesFromObj(obj[key]);
}).reduce((acc,v) => {
return acc.concat(v);
},[]);
}
const r2 = getValuesFromObj(arr1);
console.log(r2);

Changing the case of JavaScript object keys

I have following object.
var obj = [{
Address1: "dd",
Address2: "qww",
BankAccNo: "44",
BankBranchCode: "44",
BloodGrp: "A+"
},
{
Address1: "dd",
Address2: "qww",
BankAccNo: "44",
BankBranchCode: "44",
BloodGrp: "A+"
}];
How can I make all of the keys uppercase?
I want to be able to access values like this : - obj[0].ADDRESS1
obj = obj.map( function( item ){
for(var key in item){
var upper = key.toUpperCase();
// check if it already wasn't uppercase
if( upper !== key ){
item[ upper ] = item[key];
delete item[key];
}
}
return item;
});
http://jsfiddle.net/07xortqy/
Loop over all the properties in the object (with for in)
Use .toUpperCase() to get the uppercase version of the property name
Copy the value from the original property to the uppercase version
delete the original property
For anyone looking for a solution working with objects, arrays, and nested objects or arrays:
// rename function depending on your needs
const capitalizeKeys = (obj) => {
const isObject = o => Object.prototype.toString.apply(o) === '[object Object]'
const isArray = o => Object.prototype.toString.apply(o) === '[object Array]'
let transformedObj = isArray(obj) ? [] : {}
for (let key in obj) {
// replace the following with any transform function
const transformedKey = key.replace(/^\w/, (c, _) => c.toUpperCase())
if (isObject(obj[key]) || isArray(obj[key])) {
transformedObj[transformedKey] = capitalizeKeys(obj[key])
} else {
transformedObj[transformedKey] = obj[key]
}
}
return transformedObj
}
const t = {
test1: 'hello',
test2: {
aa: 0,
bb: '1',
cc: [ 3, '4', 'world']
},
test3: [{
aa: 5,
bb: '6'
}, {
cc: [ 'hello', 'world', 7 ]
}
]
}
console.log(JSON.stringify(capitalizeKeys(t)))
(this function is to be adapted since I only had to capitalize the first letter, and there is no need for the helper functions to be nested)
$.each(obj, function(i, parent) {
$.each(parent, function(key, record) {
parent[ key.toUpperCase() ] = record[key]; //rename key
delete parent[key]; //delete old key
});
});
let obj = [
{ Address1: "dd",Address2: 'qww',BankAccNo: 44,BankBranchCode: 44,BloodGrp: 'A+' },
{ Address1: "dd",Address2: 'qww',BankAccNo: 44,BankBranchCode: 44,BloodGrp: 'A+' }
];
const uppercaseKeys = (elem) => {
let newObject = {}
Object.keys(elem).reduce( (acc, key, allKeys) => {
acc[key.toUpperCase()] = elem[key]
delete elem[key]
return acc
}, elem)
return newObject
}
obj.forEach( o => uppercaseKeys )
console.log(obj)
You can now also use Object.fromEntries() in combination with Object.entries() - have a look at the Object transformations section.
const obj2 = obj1.map(item => Object.fromEntries(Object.entries(item).map(([key, val]) => [
key.toUpperCase(),
val
])));
I've detailed the steps below:
// Iterate through each item in array
const obj2 = obj1.map(item => {
// Object.entries() method returns array of object's own enumerable string-keyed property [key, value] pairs,
// in the same order as that provided by a for...in loop
const entries = Object.entries(item);
// Convert keys to uppercase
const uppercaseEntries = entries.map(([key, val]) => [
key.toUpperCase(),
val
]);
// Object.fromEntries() method transforms a list of key-value pairs into an object.
return Object.fromEntries(uppercaseEntries);
});`
https://jsfiddle.net/buj5y32x/3/
For wider support, you are better off using Object.keys() with Array.reduce().
const obj2 = obj1.map(item =>
Object.keys(item).reduce((accumulator, key) => {
// accumulator is the new object we are creating
accumulator[key.toUpperCase()] = item[key];
return accumulator;
}, {})
);
https://jsfiddle.net/qf81ezsy/
You could just loop through them and add new entries?
for (index in obj) {
for (key in obj[index]) {
obj[index][key.toUpperCase()] = obj[key];
}
}

Javascript Nested Literal to string

I am looking for a technique to run over a object of nested properties and wish to join the properties'.
This is the object I'd like to join:
var array = {
prop1: {
foo: function() {
// Your code here
}
},
prop2: {
bar1: 'some value',
bar2: 'some other value'
}
};
The result should look like this:
[
[ 'prop1', 'foo' ],
[ 'prop2', 'bar1' ],
[ 'prop2', 'bar2' ]
]
Then I'd like to join the array to strings formatted like this:
prop1.foo
prop2.bar1
prop2.bar2
Any tips?
EDIT: Forgot to say it should work for deeper arrays too.
Something along these lines? http://jsfiddle.net/X2X2b/
var array = {
prop1: {
foo: function() {
// Your code here
}
},
prop2: {
bar1: 'some value',
bar2: 'some other value'
}
};
var newA = [],
newB = [];
for ( var obj in array ) {
for (var inObj in array[obj]) {
newA.push([obj, inObj]);
newB.push(obj + '.' + inObj);
}
}
console.log(newA);
console.log(newB);
This is quite a different problem now that you have specified that it needs to support arbitrary depths. In order to solve it we need to use recursion and we need to use a second recursive parameter which keeps track of where we are in the nested hierarchy.
function objectPropertiesToArrays(obj, prepend) {
// result will store the final list of arrays
var result = [];
// test to see if this is a valid object (code defensively)
if(obj != null && obj.constructor === Object) {
for (var propertyName in obj) {
var property = obj[propertyName],
// clone prepend instantiate a new array
list = (prepend || []).slice(0);
// add the property name to the list
list.push(propertyName);
// if it isn't a nested object, we're done
if (property.constructor !== Object) {
result.push(list);
// if it is a nested object, recurse
} else {
// recurse and append the resulting arrays to our list
result = result.concat(objectPropertiesToArrays(property, list));
}
}
}
return result;
}
Example:
var obj = {
prop1: {
foo: function() { }
},
prop2: {
bar1: 'some value',
bar2: 'some other value'
},
prop3: {
x: {
y: [],
z: 'test'
},
erg: 'yar'
}
};
objectPropertiesToArrays(obj);
Returns
[
["prop1", "foo"],
["prop2", "bar1"],
["prop2", "bar2"],
["prop3", "x", "y"],
["prop3", "x", "z"],
["prop3", "erg"]
]

Categories

Resources