Optimal method for parsing a keyed JSON object - javascript

I have objects being sent to me from an API (which cannot change) that follow this type of pattern:
{
aaa: {
name: "Aaa",
desc: "..."
},
bbb: {
name: "Bbb",
desc: "..."
},
ccc: {
name: "Ccc",
desc: "...",
stuff: {
foo: {
bar: "bar"
},
keys: ["foo"]
}
},
keys: ["aaa", "bbb", "ccc"]
}
What I'm trying to do is "flatten" these objects into more usable ones on the client. So the result of flattening the object above would be:
[
{
key: "aaa",
name: "Aaa",
desc: "..."
},
{
key: "bbb",
name: "Bbb",
desc: "..."
},
{
key: "ccc",
name: "Ccc",
stuff: [
{ key: "foo", bar: "bar" }
]
}
]
As you can see there can be 2 levels of data I need to traverse.
Currently I'm looping through and creating the new object (brief ex. below), I'm just wondering if there's a more elegant way to handle this?
var myObjs = [];
$(data.keys).each(function(i, objKey) {
i = $.extend({}, data[objKey], { key: objKey });
myObjs.push(i);
});

This seems to do what you require:
function flatten(obj) {
return $.map(obj.keys, function(key) {
var res = $.extend({}, obj[key], {key: key});
for (var key in res) {
if (typeof res[key] === 'object') {
res[key] = flatten(res[key]);
}
}
return res;
});
}
See http://jsfiddle.net/alnitak/pgq2S/
NB: this will keep on recursing so long as you have nested objects - it doesn't currently stop at two levels.

This should do it:
function toArray(obj) {
if (typeof obj !== 'object' || !("keys" in obj))
return obj;
return obj.keys.map(function(key) {
var sub = obj[key];
if (typeof sub !== 'object')
return sub;
for (var prop in sub)
sub[prop] = toArray(sub[prop]);
sub.key = key;
return sub;
});
}

Related

How to restructure all data-items of an array by grouping them via main-key and collecting all of a group's remaining entries as grouped value-lists?

I am having a problem with merging objects in an array by key and the grouping the values that have the same key in arrays. Example:
let a = [{
key: "1",
value: "a",
value2: "b"
}, {
key: "2",
value: "a",
value2: "b"
}, {
key: "1",
value: "b",
value2: "a"
}, {
key: "1",
value: "c",
value2: "d"
}]
The results that I want to achieve is:
[{
key: "1",
value: ["a", "b", "c"],
value2: ["b", "a", "d"]
}, {
key: "2",
value: ["a"],
value2: ["b"]
}]
Does anyone have a simple solution for this in javascript/typescript?
I have read quite a few threads about similar issues, but none that are the exact same as the one I have above.
Thanks in advance!
You could try like this. It first reduces the data to a key based object and transform it to your array after.
var a = [{key:"1", value:"a",value2:"b"}, {key:"2", value:"a",value2:"b"}, {key:"1", value:"b",value2:"a"},{key:"1", value:"c",value2:"d"}]
a = a.reduce((acc, curr) => {
if (!acc[curr.key]) {
acc[curr.key] = { value: [curr.value], value2: [curr.value2] };
} else {
acc[curr.key].value.push(curr.value);
acc[curr.key].value2.push(curr.value2);
}
return acc;
}, {});
a = Object.entries(a).map(([key, value]) => ({ key, ...value }))
console.log(a);
let a = [{key:"1", value:"a",value2:"b"}, {key:"2", value:"a",value2:"b"}, {key:"1", value:"b",value2:"a"},{key:"1", value:"c",value2:"d"}]
const result = a.reduce((res, item) => {
let trans = res.find(e => e.key === item.key) || res.push(item) && item ;
Object.entries(item).map(([key, val]) => {
if(key !== "key") {
trans[key] = Array.isArray(trans[key]) ? [...trans[key], val]: [val];
}
});
return res;
}, []);
console.log('result', result);
A generic approach is implemented in a way that it just considers the main data structure of a common transformation task. It will be always agnostic about the (final) grouping key, but it does implement a configuration option for the latter.
Thus one might come up with an approach which is based on two reduce methods, one for collecting the groups by the provided group-key value and another one for collecting entry-values into (grouped) lists ...
function collectEntryValueAsListItem(accumulator, entry) {
const [key, value] = entry;
const valueList = accumulator[key] || (accumulator[key] = []);
valueList.push(value);
return accumulator;
}
function groupDataByKeyAndCollectEntryValues(collector, item) {
const { key, index, list } = collector;
const groupKey = item[key];
let groupItem = index[groupKey];
if (!groupItem) {
groupItem = index[groupKey] = { [key]: groupKey };
list.push(groupItem);
}
item = Object.assign({}, item);
delete item[key];
Object
.entries(item)
.reduce(collectEntryValueAsListItem, groupItem);
return collector;
}
const sampleList = [
{ key: "1", value: "a", value2: "b" },
{ key: "2", value: "a", value2: "b" },
{ key: "1", value: "b", value2: "a" },
{ key: "1", value: "c", value2: "d" },
];
console.log(
'... the result which the OP was looking for ...',
sampleList.reduce(groupDataByKeyAndCollectEntryValues, {
key: 'key',
index: {},
list: [],
}).list
);
console.log(
'... 2nd proof of the generic reduce approach ...',
sampleList.reduce(groupDataByKeyAndCollectEntryValues, {
key: 'value',
index: {},
list: [],
}).list
);
console.log(
'... 3rd proof of the generic reduce approach ...',
sampleList.reduce(groupDataByKeyAndCollectEntryValues, {
key: 'value2',
index: {},
list: [],
}).list
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Can we assign an object key to another variable like this? [duplicate]

Given a JavaScript object, how can I convert it into an array of objects (each with key, value)?
Example:
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
resulting like:
[
{ key: 'firstName', value: 'John' },
{ key: 'lastName', value: 'Doe' },
{ key: 'email', value: 'john.doe#gmail.com' }
]
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
var output = Object.entries(data).map(([key, value]) => ({key,value}));
console.log(output);
Inspired By this post
Using map function
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = Object.keys(data).map(key => ({ key, value: data[key] }));
console.log(result);
You can just iterate over the object's properties and create a new object for each of them.
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = [];
for(var key in data)
{
if(data.hasOwnProperty(key))
{
result.push({
key: key,
value: data[key]
});
}
}
The previous answer lead me to think there is a better way...
Object.keys(data).map(function(key) {
return { key, value: data[key] };
});
or in ES6 using arrow functions:
Object.keys(data).map((key) => ({ key, value: data[key] }));
Just make your life easier and use es6 syntax with a map
var output = Object.keys(data).map(key => {
return {
key: key,
value: data[key]
};
})
var result = [];
for(var k in data) result.push({key:k,value:data[k]});
Or go wild and make the key and value keys customizable:
module.exports = function objectToKeyValueArray(obj, keyName = 'key', valueName = 'value') {
return Object
.keys(obj)
.filter(key => Object.hasOwnProperty.call(obj, key))
.map(key => {
const keyValue = {};
keyValue[keyName] = key;
keyValue[valueName] = obj[key];
return keyValue;
});
};
An alternative method for doing this that works on multi level objects and does not use recursion.
var output = []
var o = {
x:0,
y:1,
z:{
x0:{
x1:4,
y1:5,
z1:6
},
y0:2,
z0:[0,1,2],
}
}
var defer = [ [ o ,[ '_root_' ] ] ]
var _defer = []
while(defer.length){
var current = defer.pop()
var root = current[1]
current = current[0]
for(var key in current ){
var path = root.slice()
path.push(key)
switch( current[key].toString() ){
case '[object Object]':
_defer.push( [ current[key] , path ] )
break;;
default:
output.push({
path : path ,
value : current[key]
})
break;;
}
}
if(!defer.length)
defer = _defer.splice(0,_defer.length)
}
[
{ path: [ '_root_', 'x' ], value: 0 },
{ path: [ '_root_', 'y' ], value: 1 },
{ path: [ '_root_', 'z', 'y0' ], value: 2 },
{ path: [ '_root_', 'z', 'z0' ], value: [ 0, 1, 2 ] },
{ path: [ '_root_', 'z', 'x0', 'x1' ], value: 4 },
{ path: [ '_root_', 'z', 'x0', 'y1' ], value: 5 },
{ path: [ '_root_', 'z', 'x0', 'z1' ], value: 6 }
]
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);
I would say to use npm package flat.
works amazing for nested objects and arrays.
var flatten = require('flat')
flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
// {
// 'key1.keyA': 'valueI',
// 'key2.keyB': 'valueII',
// 'key3.a.b.c': 2
// }
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);

Override all keys of an object but its id

I have a similar object as this one but it has quite more keys. I want to update all of its keys but id. I can do so manually. But I think it is not the best way.
const o = {
name: "unknow",
key: "key"
value: "value"
id ": 12
}
How can I update/override all keys of an object but id?
Update
The two object has the same keys. But their keys have different value. I need to update all keys of the first object excluding its id.
I suspect that you're looking for something like assignBut: it sets properties of ob on oa but the specified one:
const assignBut = (prop, oa, ob) => {
for (let key of Object.keys(ob))
// Check that I also verify that the property
// to set should be part of "oa" object. This
// prevents adding new properties: it just updates
// existing ones.
if (key !== prop && oa.hasOwnProperty(key))
oa[key] = ob[key]
}
const oa = {
name: "unknow",
key: "key",
value: "value",
id: 12
}
const ob = {
name: "xxx",
key: "yyy",
value: "zzz",
other: "aaa",
yetAnother: 289,
id: 15
}
assignBut('id', oa, ob)
console.log(oa)
Another approach to omit a given property
One may take advantage of destructuring and computed property names to omit the whole given property so the for..of just needs to check that each property from ob is present in oa to set it.
Also, one may save the check to verify that a property from ob exists in oa performing an intersection of oa and ob keys:
const oa = {
name: "unknow",
key: "key",
value: "value",
id: 12
}
const ob = {
name: "xxx",
key: "yyy",
value: "zzz",
other: "aaa",
yetAnother: 289,
id: 15
}
const intersect = (xs, ys) => xs.filter(x => ys.includes(x))
const assignBut = (prop, oa, {
[prop]: omitted,
...ob
}) => {
const sharedKeys = intersect(Object.keys(oa), Object.keys(ob))
for (let key of sharedKeys)
oa[key] = ob[key]
}
assignBut('id', oa, ob)
console.log(oa)
You can iterate through Object.keys like below -
const o = {
name: "unknow",
key: "key",
value: "value",
id : 12
};
Object.keys(o).forEach((key)=>{
if(key !=="id"){
console.log(o[key]) //value
}
}
);
Following approach is based on lodash. If you are not comfortable using a library, please ignore.
Benefit of omit is that you can pass an array of keys and ignore multiple keys.
There is also a function called pick where you can only pick certain properties you need.
_.omit
const o = { name: "unknow", key: "key", value: "value", id: 12 }
const props = { name: "foo", key: "key2", value: "bar", id: 15 };
const final = _.assign({}, o, _.omit(props, 'id'));
console.log(final)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
_.pick
const o = { name: "unknow", key: "key", value: "value", id: 12 }
const props = { name: "foo", key: "key2", value: "bar", id: 15, test: 'abc', hello: 'world' };
const final = _.assign({}, o, _.pick(props, ['name', 'key', 'value']));
console.log(final)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
pure js implementation
const o = { name: "unknow", key: "key", value: "value", id: 12 }
const propsOmit = { name: "foo", key: "key2", value: "bar", id: 15 };
const propsPick = { name: "foo", key: "key2", value: "bar", id: 15, test: 'abc', hello: 'world' };
const finalOmit = Object.assign({}, o, omit(propsOmit, 'id'));
const finalPick = Object.assign({}, o, omit(propsPick, ['id', 'test', 'hello']));
console.log(finalOmit)
console.log(finalPick)
function omit(obj, ignoreKeys) {
if (!Array.isArray(ignoreKeys)) {
ignoreKeys = [ ignoreKeys ];
}
const copy = Object.assign({}, obj);
ignoreKeys.forEach((k) => delete copy[k]);
return copy;
}
function pick(obj, selectKeys) {
if (!Array.isArray(selectKeys)) {
selectKeys = [ selectKeys ];
}
const copy = {};
ignoreKeys.forEach((k) => copy[k] = obj[k]);
return copy;
}
References:
_.assign
_.omit
_.pick

Passing an array to JavaScript filter function

Let's say I have an array of objects like this:
someObj: [
{
name: "Obj1",
type: "a"
},
{
name: "Obj2",
type: "b"
},
{
name: "Obj3",
type: "c"
}
]
Now I have a function that should return an array of objects from the someObj array if the parameters passed have the same type property. The thing is the parameter passed is an array and I'm not sure how to make that comparison.
function filter(types) {
var filtered = someObj.filter(function(item) {
return item.type == ???
});
return filtered;
}
filter(["a", "c"]);
How do I compare each item in the array parameter that's passed to item.type so that if they're equal, then the filter function would return to me an array like so:
[
{
name: "Obj1",
type: "a"
},
{
name: "Obj3",
type: "c"
}
]
You could search for the type.
ES5 with Array#indexOf
return types.indexOf(item.type) !== -1;
ES6 with Array#includes
return types.includes(item.type);
You can use Array#includes() to check the type:
let someObj = [
{name: "Obj1",type: "a"},
{name: "Obj2",type: "b"},
{name: "Obj3",type: "c"}
]
function filter(types) {
return someObj.filter(function(item) {
return types.includes(item.type)
});
}
console.log(filter(["a", "c"]));
Or a long winded mildly old-fashioned way:
var someObj = [
{
name: "Obj1",
type: "a"
},
{
name: "Obj2",
type: "b"
},
{
name: "Obj3",
type: "c"
}
];
function filter(types) {
var filtered = someObj.filter(function(item) {
if ( types.indexOf(item.type) >= 0 )
{
return item;
}
});
return filtered;
}
var x = filter(["a", "c"]);
console.log(x);
If you can use ES6 syntax you could use the following:
var someObj = [
{
name: "Obj1",
type: "a"
},
{
name: "Obj2",
type: "b"
},
{
name: "Obj3",
type: "c"
}
]
function filter(types){
return someObj.filter(obj => types.includes(obj.type))
}
console.log(filter(["a", "c"]))
You can use a reduce function here:
function filter(someObj, types) {
return someObj.reduce((acc, obj) => {
return types.includes(obj.type)? [...acc, obj] : acc
}, [])
}

Change all null to '' in array of Objects (javascript)

I have this array of objects shown below
Object {Results:Array[3]}
Results:Array[3]
[0-2]
0:Object
id=null
name: "Rick"
Value: "34343"
1:Object
id=2
name: null
Value: "2332"
2:Object
id=2
name:'mike'
Value: null
As you can see, in 1 object i have id as null, 2nd object has name as null and 3rd object has Value as null. Each object has some property as null.
I want to loop through all of these and replace null with ''. Can someone let me know how to achieve this...
Here's something quick:
var results = [{
id: null,
name: "Rick",
Value: "34343"
}, {
id: 2,
name: null,
Value: "2332"
}, {
id: 2,
name: 'mike',
Value: null
}];
results.forEach(function(object){
for(key in object) {
if(object[key] == null)
object[key] = '';
}
});
console.log(JSON.stringify(results, null, 2))
You only needed to Google looping through objects. Here's an example:
Looping through every key (if your keys are unknown or you want to do the same for all keys)
for (const obj of arr) {
if (typeof obj !=== 'object') continue;
for (k in obj) {
if (!obj.hasOwnProperty(k)) continue;
v = obj[k];
if (v === null || v === undefined) {
obj[k] = '';
}
}
}
If your keys are known:
for (const obj of arr) {
if (obj.name === undefined || obj.name === null) {
obj.name = '';
}
}
You could iterate over the elements of the array and over the keys and assign the wanted strings if necessary.
var data = { Results: [{ id: null, name: "Rick", Value: "34343" }, { id: 2, name: null, Value: "2332" }, { id: 2, name: 'mike', Value: null }] };
data.Results.forEach(function (o) {
Object.keys(o).forEach(function (k) {
if (o[k] === null) {
o[k] = '';
}
});
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can look over your array and all keys and if the property contains null replace it with empty string, e.g.:
var arr = [
{ id=null name: "Rick" Value: "34343" },
{ id=2 name: null Value: "2332" },
{ id=2 name:'mike' Value: null }
];
arr.forEach(function(o) {
Object.keys(o).forEach(function(key) {
id (o[key] === null) {
o[key] = '';
}
});
});

Categories