map array of objects based on set of properties - javascript

Suppose I have an object:
let array = [
{a: 1, b: 5, c: 9},
{a: 2, b: 6, c: 10},
{a: 3, b: 7, c: 11},
{a: 4, b: 8, c: 12}
];
then I have a dictionary:
const columns = [
{ key: 'a', value: 'a' },
{ key: 'b', value: 'b' },
]
I want to filter out properties that are not defined in columns.
I have tried
array.map((x) => ({"a": x.a, "b": x.b}))
Is there a way to use the data defined in columns instead of manually typing all the properties?
Desired output:
[
{
"a": 1,
"b": 5
},
{
"a": 2,
"b": 6
},
{
"a": 3,
"b": 7
},
{
"a": 4,
"b": 8
}
]

You could map entries and get the new objects.
let
array = [{ a: 1, b: 5, c: 9 }, { a: 2, b: 6, c: 10 }, { a: 3, b: 7, c: 11 }, { a: 4, b: 8, c: 12 }],
columns = [{ key: 'a', value: 'a' }, { key: 'b', value: 'b' }],
keys = columns.map(({ key }) => key),
result = array.map(o => Object.fromEntries(keys.map(k => [k, o[k]])));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You could use this.
This uses just an array to hold the desired columns because I don't get why you would use a dictionary with key and value being the same.
let array = [
{ a: 1, b: 5, c: 9 },
{ a: 2, b: 6, c: 10 },
{ a: 3, b: 7, c: 11 },
{ a: 4, b: 8, c: 12 },
];
const desiredColumns = ["a", "b"];
const transformed = array.map(item => {
const obj = {};
desiredColumns.forEach(col => {
if(col in item){
obj[col] = item[col];
}
})
return obj;
})
console.log(array);
console.log(transformed)

Another, slightly less direct way using map() and reduce():
Create an array with all the keys we'll keep
Reduce the array to get the desired result
Add current key + value if key keep array
const array = [{a: 1, b: 5, c: 9}, {a: 2, b: 6, c: 10}, {a: 3, b: 7, c: 11}, {a: 4, b: 8, c: 12} ];
const columns = [{ key: 'a', value: 'a' }, { key: 'b', value: 'b' }, ];
const toKeep = columns.map(({ key }) => key).flat();
const result = array.map(a =>
Object.keys(a)
.reduce((prev, cur) => (toKeep.includes(cur)) ? { ...prev, [cur]: a[cur] } : prev, {})
);
console.log(result);
Result:
[
{
"a": 1,
"b": 5
},
{
"a": 2,
"b": 6
},
{
"a": 3,
"b": 7
},
{
"a": 4,
"b": 8
}
]

Related

Flatten nested objects, keeping the properties of parents

I have a data structure that has this shape:
[
{
a: "x",
val: [
{ b: "y1", val: [1, 2, 3] },
{ b: "y2", val: [4, 5, 6] },
],
},
];
An example with 3 levels:
[
{
a: "x",
val: [
{ b: "y1", val: [
{c: "z1", val: [1, 2]}
] },
{ b: "y2", val: [
{ c: "z2", val: [3, 4] },
{ c: "z3", val: [5, 6, 7] },
{ c: "z4", val: [8] }
] },
],
},
];
Each object always has the same level of nesting, and I know the max depth of nesting in advance. We also know the names of the keys in advance: we know that the key at level 1 will be named a, the one at level 2 will be named b, and so on.
I'm looking to create a function that transforms the first example into:
[
{
a: "x",
b: "y1",
val: [1, 2, 3],
},
{
a: "x",
b: "y2",
val: [4, 5, 6],
},
];
that is, a flat array with values and keys inherited from parents.
I've got a solution which works for the first example:
const res = [
{
a: "x",
val: [
{ b: "y1", val: [1, 2, 3] },
{ b: "y2", val: [4, 5, 6] },
],
},
].flatMap((x) => x.val.flatMap((d) => ({ a: x.a, ...d })));
console.log(res);
but I'm struggling to turn it into a recursive function.
Thank you in advance for your help!
You could have a look to the arrays and if no objects inside return an object, otherwise map val property by storing other properties.
const
isObject = o => o && typeof o === 'object',
flat = array => {
if (!array.every(isObject)) return { val: array };
return array.flatMap(({ val, ...o }) => {
const temp = flat(val);
return Array.isArray(temp)
? temp.map(t => ({ ...o, ...t }))
: { ...o, ...temp };
});
},
data0 = [{ a: "x", val: [{ b: "y1", val: [1, 2, 3] }, { b: "y2", val: [4, 5, 6] }] }],
data1 = [{ a: "x", val: [{ b: "y1", val: [{ c: "z1", val: [1, 2] }] }, { b: "y2", val: [{ c: "z2", val: [3, 4] }, { c: "z3", val: [5, 6, 7] }, { c: "z4", val: [8] }] }] }];
console.log(flat(data0));
console.log(flat(data1))
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to reshaping Data in Javascript from array to object without losing some of the data

I am trying to add the key to each so that I can be able to easy make a multi scatter plot in d3. . I am not sure how to do it.
EDIT: TO CLARIFY what I meant.
Data:
var dataOriginal = {
Apples: [{"A":4,"B":null,"C":null,"D":2}, {"A":5,"B":null,"C":3,"D":2}],
Oranges: [{"A":3,"B":1,"C":4,"D":4.3}],
Jackfruit: [{"A":5,"B":4,"C":4,"D":3}],
Avocado: [{"A":null,"B":33,"C":2,"D":9.66}],
Pomegranate: [{"A":5,"B":3.5,"C":null,"D":6}]
}
Function:
const data = Object.keys(dataOriginal).map((key) => {
const temp = {...dataOriginal[key]};
temp.key = key;
return temp;
});
Results:
0:
0: {A: 4, B: null, C: null, D: 2}
1: {A: 5, B: null, C: 3, D: 2}
key: "Apples"
__proto__: Object
1:
0: {A: 3, B: 1, C: 4, D: 4.3}
key: "Oranges"
__proto__: Object
2:
0: {A: 5, B: 4, C: 4, D: 3}
key: "Jackfruit"
__proto__: Object
3:
0: {A: null, B: 33, C: 2, D: 9.66}
key: "Avocado"
__proto__: Object
4: {0: {…}, key: "Pomegranate"}
Desired results
: {A: 4, B: null, C: null, D: 2, key: "Apples"}
1: {A: 3, B: 1, C: 4, D: 4.3, key: "Oranges"}
2: {A: 5, B: 4, C: 4, D: 3, key: "Jackfruit"}
3: {A: null, B: 33, C: 2, D: 9.66, key: "Avocado"}
4: {A: 5, B: 3.5, C: null, D: 6, key: "Pomegranate"}
5: {A:5,B:null,C:3,D:2, key: "Apples"}
You need to reduce the object to get a single object with added values.
const
addByKey = array => array.reduce((a, b) => {
Object.entries(b).forEach(([k, v]) => a[k] = (a[k] || 0) + v);
return a;
}, {}),
dataOriginal = { Apples: [{ A: 4, B: null, C: null, D: 2 }, { A: 5, B: null, C: 3, D: 2 }], Oranges: [{ A: 3, B: 1, C: 4, D: 4.3 }], Jackfruit: [{ A: 5, B: 4, C: 4, D: 3 }], Avocado: [{ A: null, B: 33, C: 2, D: 9.66 }], Pomegranate: [{ A: 5, B: 3.5, C: null, D: 6 }] }
data = Object.keys(dataOriginal).map((key) => ({ ...addByKey(dataOriginal[key]), key }));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
For getting single object with same keys, you could map the objects, add the key and get a flat array.
const
dataOriginal = { Apples: [{ A: 4, B: null, C: null, D: 2 }, { A: 5, B: null, C: 3, D: 2 }], Oranges: [{ A: 3, B: 1, C: 4, D: 4.3 }], Jackfruit: [{ A: 5, B: 4, C: 4, D: 3 }], Avocado: [{ A: null, B: 33, C: 2, D: 9.66 }], Pomegranate: [{ A: 5, B: 3.5, C: null, D: 6 }] }
data = Object
.keys(dataOriginal)
.flatMap(key => dataOriginal[key].map(o => ({ ...o, key })));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The reason why {"A":5,"B":null,"C":3,"D":2} is missed is because, index 0 is hardcoded in the code.
const temp = {...dataOriginal[key][0]};
Alternate solution:
var dataOriginal = {
Apples: [{"A":4,"B":null,"C":null,"D":2}, {"A":5,"B":null,"C":3,"D":2}],
Oranges: [{"A":3,"B":1,"C":4,"D":4.3}],
Jackfruit: [{"A":5,"B":4,"C":4,"D":3}],
Avocado: [{"A":null,"B":33,"C":2,"D":9.66}],
Pomegranate: [{"A":5,"B":3.5,"C":null,"D":6}]
}
const myData =[]
Object.keys(dataOriginal).map((key) => {
for (let i = 0; i < dataOriginal[key].length; i++) {
myData.push({...dataOriginal[key][i], key})
}
})
console.log(myData)

Improve Array transformation in Javascript

Suppose I have a input array like the following
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
]
I want to transform my inputArray to the following outputArray
var outputArray = [
{
a: 1,
b: 1,
c: 1,
d: 1,
values:{
"1":{value: 1},
"2":{value: 2},
"3":{value: 3},
"4":{value: 4},
"5":{value: 5}
}
},
{
a: 1,
b: 2,
c: 1,
d: 1,
values:{
"1":{value: 1},
"2":{value: 2},
"3":{value: 3},
"4":{value: 4},
"5":{value: 5}
}
}
]
That means, I need create a dictionary for same property of a, b, c and d where the value of property rank is the key of the dictionary and the value of the dictionary is an object where the only property is value.
We assume that the inputArray will not be sorted with respect to the combination of a, b, c and d. So, my approach is like this,
(function(){
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
]
var temp = inputArray.sort(function(valA, valB){
if(valA.a === valB.a){
if(valA.b === valB.b){
if(valA.c === valB.c){
return valA.d < valB.d;
}
return valA.c < valB.c;
}
return valA.b < valB.b;
}
return valA.a < valB.a;
});
var outputArray = [],
currentIndex = 0;
for(var i = 0; i < inputArray.length; i++){
if(i > 0 && isConfigurationSame(inputArray[i], inputArray[i-1])){
outputArray[currentIndex-1].values[inputArray[i].rank] = {
value: inputArray[i].value
}
}
else{
outputArray.push(mapToOutputArrayObject(inputArray[i]));
currentIndex++;
}
}
console.log(outputArray);
function isConfigurationSame(A, B) {
return A.a === B.a
&& A.b === B.b
&& A.c === B.c
&& A.d === B.d;
}
function mapToOutputArrayObject(val){
var row = {};
row['a'] = val.a;
row['b'] = val.b;
row['c'] = val.c;
row['d'] = val.d;
row['values'] = {};
row.values[val.rank] = {
value: val.value
}
return row;
}
}());
But the problem is, this thing is really getting more time if the length of input array is huge. This multi-criteria sorting also taking much time.
Is there any better approach to accomplish the result more efficiently with less time?
Thanks for your time and patience.
UPDATE: The values of a, b, c and d can be integer or null.
You could create a hashtable and generate a unique key based on a, b, c and d:
const hash = {};
for(const { a, b, c, d, value, rank } of array) {
const key = JSON.stringify([a, b, c, d]); // generate a unique, but not random key
if(hash[key]) { // check if it already exists,
hash[key].values[rank] = value; // merge
} else {
hash[key] = { // create a new entry
a, b, c, d,
values: { [rank]: value },
};
}
}
const result = Object.values(hash); // turn the object into an array
That is O(n), which is better as the time complexity of any .sort implementation (but it only works if a, b, c and d are serializable (like in this case)).
You could take a Map and an array of the grouping keys and collect the values for each group.
var array = [{ a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1 }, { a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2 }, { a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3 }, { a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4 }, { a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5 }, { a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1 }, { a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2 }, { a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3 }, { a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4 }, { a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5 }],
keys = ['a', 'b', 'c', 'd'],
result = [],
map = new Map;
array.forEach(o => {
var key = keys.map(k => o[k]).join('|'),
temp = map.get(key);
if (!temp) {
map.set(key, temp = Object.assign(...keys.map(k => ({ [k]: o[k] })), { values: {} }));
result.push(temp);
}
temp.values[o.rank] = { value: o.value };
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a stab at it using Set, Map and a const method to build the Values object.
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
];
const getValueObject = (a,b,c,d, arr) => {
let obj = {};
arr.filter(i => i.a === a &&
i.b === b &&
i.c ===c &&
i.d === d)
.forEach(item => obj[item.value] = item.rank);
return obj;
};
// Get a set based on the key a,b,c,d
let newArray = [...new Set(inputArray.map(({a,b,c,d}) => `${a},${b},${c},${d}`))]
.map(item => {
let [a,b,c,d] = item.split(',').map(i => parseInt(i));
// filter and add
return {
a: a,
b: b,
c: c,
d: d,
values: getValueObject(a,b,c,d, inputArray)
};
});
console.log(newArray);
Here is another option, first grouping by a, b, c and d. Then mapping over each group transforming the value and rank.
var inputArray = [{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1}, {a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2}, {a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3}, {a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4}, {a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5}, {a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1}, {a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2}, {a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3}, {a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4}, {a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}];
function groupBy(array, callback) {
return array.reduce((groups, item, ...args) => {
const key = callback(item, ...args),
group = groups[key] || (groups[key] = []);
group.push(item);
return groups;
}, {});
};
console.log(
Object
.values( groupBy(inputArray, ({a, b, c, d}) => [a, b, c, d]) )
.map(group => {
const {a, b, c, d} = group[0],
values = {};
group.forEach(({value, rank}) => values[rank] = {value});
return {a, b, c, d, values};
})
);

Adding a new element to an object in an array

Lets say I have and array made up of objects:
var points = [
{ id: 1, a: 0, b: 3 },
{ id: 2, a: 4, b: -1 },
{ id: 3, a: -1, b: 5 },
{ id: 4, a: 41, b: 2 },
{ id: 5, a: 69, b: 3 },
]
I want to iterate through each item and add a + b to get a new item d. I then want to add d within each object in the array to get a new value. When I try the below, it just adds 5 extra objects rather than appending the new element (key=value, ex: d: 3) to each individual object. What am I doing wrong here?
points.forEach((item) => {
var d = Math.abs(item.x) + Math.abs(item.y);
console.log(d);
points.item.push('d: ' + d);
});
Try following
var points = [{ id: 1, a: 0, b: 3 },{ id: 2, a: 4, b: -1 },{ id: 3, a: -1, b: 5 },{ id: 4, a: 41, b: 2 },{ id: 5, a: 69, b: 3 }];
points.forEach(o => o.d = Math.abs(o.a) + Math.abs(o.b));
console.log(points);
#jcbridwe, you can use assign() method on Object to add missing property from source object to target object.
Please have a look at the below code.
Try the below code online at http://rextester.com/EPHYV10615.
var points = [
{ id: 1, a: 0, b: 3 },
{ id: 2, a: 4, b: -1 },
{ id: 3, a: -1, b: 5 },
{ id: 4, a: 41, b: 2 },
{ id: 5, a: 69, b: 3 },
]
for(var index in points){
var a = points[index].a;
var b = points[index].b;
Object.assign(points[index], {d: a+b});
}
console.log(points);
» Output
[ { id: 1, a: 0, b: 3, d: 3 },
{ id: 2, a: 4, b: -1, d: 3 },
{ id: 3, a: -1, b: 5, d: 4 },
{ id: 4, a: 41, b: 2, d: 43 },
{ id: 5, a: 69, b: 3, d: 72 } ]
Mutable approach:
points.forEach(o => o.d = o.a + o.b);
Immutable approach:
const newPoints = points.map(o => Object.assign({}, o, {d: o.a + o.b}))

Elegant array transformation in Javascript

What's an elegent way - purely functional, ideally - to transform (reduce?) this array:
var in = [
{ a: 1, b: 'x', c: 'foo' },
{ a: 1, b: 'y', c: 'goo' },
{ a: 2, b: 'x', c: 'hoo' },
{ a: 2, b: 'y', c: 'joo' }
]
Into this:
var out = [
{ a: 1, x: 'foo', y: 'goo' },
{ a: 2, x: 'hoo', y: 'joo' }
]
The logic is that all elements should be joined based on their a property, and all b and c properties denote key/value pairs respectively that should be merged into the single object based on their shared a value.
You can use a hash object, and reduce to wrap the hashing like this:
const arr = [
{ a: 1, b: 'x', c: 'foo' },
{ a: 1, b: 'y', c: 'goo' },
{ a: 2, b: 'x', c: 'hoo' },
{ a: 2, b: 'y', c: 'joo' }
];
let result = Object.values( // the result is the values of the hash object
arr.reduce((hash, o) => { // hash is a hash object that make it easier to group the result
hash[o.a] = hash[o.a] || {a: o.a}; // if there is no object in the hash that have the value of the key a equal to o.a, then create a new one
hash[o.a][o.b] = o.c; // set the value of the key stored in o.b to o.c
return hash;
}, {})
);
console.log(result);
You could use a closure with a Map
var input = [{ a: 1, b: 'x', c: 'foo' }, { a: 1, b: 'y', c: 'goo' }, { a: 2, b: 'x', c: 'hoo' }, { a: 2, b: 'y', c: 'joo' }],
output = input.reduce((map => (r, o) => (!map.has(o.a) && map.set(o.a, r[r.push({ a: o.a }) - 1]), map.get(o.a)[o.b] = o.c, r))(new Map), []);
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use forEach and Object.assign to group by a and then map to return object values.
var data = [
{ a: 1, b: 'x', c: 'foo' },
{ a: 1, b: 'y', c: 'goo' },
{ a: 2, b: 'x', c: 'hoo' },
{ a: 2, b: 'y', c: 'joo' }
]
var r = {}
data.forEach(e => r[e.a] = Object.assign((r[e.a] || {}), {a: e.a, [e.b]: e.c}))
r = Object.keys(r).map(e => r[e])
console.log(r)
I like provided answers, but here is my attempt. I believe it's more readable, but it uses Object.assign and Object.values
const input = [
{ a: 1, b: 'x', c: 'foo' },
{ a: 1, b: 'y', c: 'goo' },
{ a: 2, b: 'x', c: 'hoo' },
{ a: 2, b: 'y', c: 'joo' }
]
const map = input.reduce((acc, obj) => {
const [a, key, value] = Object.values(obj)
const newObj = {a, [key]: value}
if (acc[a]) {
Object.assign(acc[a], newObj)
} else {
acc[a] = newObj
}
return acc
}, {})
console.log(Object.values(map))
Not sure if approach is elegant or functional, though returns expected result using for..of loops, Array.prototype.some() and Object.assign()
function props(array, key, prop1, prop2) {
let arr = [];
for (let obj of array) {
let o = {};
for (let {[key]:_key, [prop1]:_prop1, [prop2]:_prop2} of [obj]) {
o[_prop1] = _prop2;
o[key] = _key;
}
if (!arr.some(p => p[key] === o[key])) arr.push(o);
for (let prop of arr) {
if (prop[key] == o[key]) {
prop = Object.assign(prop, o)
}
}
}
return arr
}
var _in = [
{ a: 1, b: 'x', c: 'foo' },
{ a: 1, b: 'y', c: 'goo' },
{ a: 2, b: 'x', c: 'hoo' },
{ a: 2, b: 'y', c: 'joo' }
];
console.log(props(_in, "a", "b", "c"));

Categories

Resources