I have like to group an array of objects.
var _ = require('lodash')
const objects = [
{ a: 'test1', b: 'sample', c: 'ten' },
{ a: 'test2', b: 'sample', c: 'ten' },
{ a: 'test3', b: 'sampleone', c: 'ten' },
{ a: 'test4', b: 'sample', c: 'tentwo' },
]
//using lodash, groupBy b and c and concat 'a' values
const grouped = _(objects)
.groupBy('b', 'c')
.map((value, key) => ({
b: key,
c: value[0].c,
a: _.map(value, 'a').join('|'),
}))
.value()
console.log(grouped)
The output from above is:
[
{ b: 'sample', c: 'ten', a: 'test1|test2|test4' },
{ b: 'sampleone', c: 'ten', a: 'test3' }
]
But I have like the output to be below, only group by b is 'sample' and c is 'ten'
[
{ b: 'sample', c: 'ten', a: 'test1|test2' },
{ b: 'sampleone', c: 'ten', a: 'test3' },
{ b: 'sample', c: 'tentwo', a: 'test4' }
]
Any help would be greatly appreciated.
To group by multiple keys you will to need do something like
.groupBy(({b,c}) => `${b}|${c}`)
i.e joining the individual keys and creating a new composite key (also will have create it in such a way that it becomes unique)
now the grouped object will look like this before the map call
{
sampleone|ten: [{
a: "test3",
b: "sampleone",
c: "ten"
}],
sample|ten: [{
a: "test1",
b: "sample",
c: "ten"
}, {
a: "test2",
b: "sample",
c: "ten"
}],
sample|tentwo: [{
a: "test4",
b: "sample",
c: "tentwo"
}]
}
const objects = [
{ a: 'test1', b: 'sample', c: 'ten' },
{ a: 'test2', b: 'sample', c: 'ten' },
{ a: 'test3', b: 'sampleone', c: 'ten' },
{ a: 'test4', b: 'sample', c: 'tentwo' },
]
const grouped = _(objects)
.groupBy(({b,c}) => `${b}|${c}`)
.map((value, key) => ({
b: value[0].b,
c: value[0].c,
a: _.map(value, 'a').join('|'),
}))
.value()
console.log(grouped)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
Related
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
}
]
I have an array of objects that has a nested array of objects in it. So the array looks something like:
const list = [
{
A: "a1",
B: "b1",
C: [
{
A: "a22",
B: "b12"
},
{
A: "a11",
B: "b11"
},
{
A: "a10",
B: "b10"
}
]
},
{
A: "a2",
B: "b2",
C: [
{
A: "a10",
B: "b10"
},
{
A: "a01",
B: "b01"
}
]
},
{
A: "a0",
B: "b0",
C: [
{
A: "a22",
B: "b22"
},
{
A: "a21",
B: "b21"
},
{
A: "a20",
B: "b20"
}
]
}
];
As can be seen I have an array of objects and each object as one or more fields that is also an array of objects. I can sort the array of objects based on one of the keys and it works just fine. What I want to do is sort by one of the keys in the nested array. For example sorting on C.A would yield something like (expected):
[
{
A: "a0",
B: "b0",
C: [
{
A: "a22",
B: "b22"
},
{
A: "a21",
B: "b21"
},
{
A: "a20",
B: "b20"
}
]
},
{
A: "a1",
B: "b1",
C: [
{
A: "a12",
B: "b12"
},
{
A: "a11",
B: "b11"
},
{
A: "a10",
B: "b10"
}
]
},
{
A: "a2",
B: "b2",
C: [
{
A: "a10",
B: "b10"
},
{
A: "a01",
B: "b01"
}
}
];
Ideas?
The way to get one's head clear on such things is to factor out the sort functions to explicitly state the objective, like this (I think I understand the objective)...
// sort a and b by the smallest value of A in their C arrays
const myCompare = (a, b) => {
return a.minAinC.localeCompare(b.minAinC);
};
// get the lexically smallest value of A in an object's C array
const minAinC = obj => {
const minC = obj.C.reduce((acc, o) => acc.A.localeCompare(o.A) > 0 ? o : acc, obj.C[0])
return minC.A;
};
// preprocess the outer array and cache a minAinC value on each, making the next sort efficient (optional)
const data = getData()
const readyToSort = data.map(o => ({ ...o, minAinC: minAinC(o) }));
const sorted = readyToSort.sort(myCompare)
console.log(sorted)
function getData() {
return [{
A: "a1",
B: "b1",
C: [{
A: "a22",
B: "b12"
},
{
A: "a11",
B: "b11"
},
{
A: "a10",
B: "b10"
}
]
},
{
A: "a2",
B: "b2",
C: [{
A: "a10",
B: "b10"
},
{
A: "a01",
B: "b01"
}
]
},
{
A: "a0",
B: "b0",
C: [{
A: "a22",
B: "b22"
},
{
A: "a21",
B: "b21"
},
{
A: "a20",
B: "b20"
}
]
}
];
}
I have this array of arrays of objects :
[
[
{a: 'FER', b: 'MEN', c: 'TUM'},
{a: 'RIS ', b: 'US', c: 'SOU'},
{a: 'CON', b: 'SEC', c: 'TETUR'}
],
[
{d: 'LIGU'},
{d: 'GU'},
{d: 'LA'}
],
[
{e: 'UL', f: 'LAM'},
{e: 'COR', f: 'PER'},
{e: 'EGE', f: 'STAS'}
]
]
What I want to obtain in the more generic manner is this (in reality, I have one array of 21 arrays with 205 objects in each):
[
{a: 'FER', b: 'MEN', c: 'TUM', d: 'LIGU', e: 'UL', f: 'LAM'},
{a: 'RIS ', b: 'US', c: 'SOU', d: 'GU', e: 'COR', f: 'PER'},
{a: 'CON', b: 'SEC', c: 'TETUR', d: 'LA', e: 'EGE', f: 'STAS'}
]
I tried so many things (object assign, reduce, etc.) but my head is a mess right now and I'm stuck on how I can merge objects in a loop.
Any help so much appreciated!
You could reduce with a mapping of objects.
const
data = [[{ a: 'FER', b: 'MEN', c: 'TUM' }, { a: 'RIS ', b: 'US', c: 'SOU' }, { a: 'CON', b: 'SEC', c: 'TETUR' }], [{ d: 'LIGU' }, { d: 'GU' }, { d: 'LA' }], [{ e: 'UL', f: 'LAM' }, { e: 'COR', f: 'PER' }, { e: 'EGE', f: 'STAS' }]],
result = data.reduce((a, b) => a.map((o, i) => ({ ...o, ...b[i] })));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Array#reduce and Array#forEach:
const data = [
[
{ a: 'FER', b: 'MEN', c: 'TUM' },
{ a: 'RIS ', b: 'US', c: 'SOU' },
{ a: 'CON', b: 'SEC', c: 'TETUR' }
],
[
{ d: 'LIGU' },
{ d: 'GU' },
{ d: 'LA' }
],
[
{ e: 'UL', f: 'LAM' },
{ e: 'COR', f: 'PER' },
{ e: 'EGE', f: 'STAS' }
]
];
const res = data.reduce((acc, arr) => {
arr.forEach((obj, i) => {
acc[i] = { ...(acc[i] || {}), ...obj };
});
return acc;
}, []);
console.log(res);
var counter = 0,
finalarr = [],
tmpobj = {};
while (counter < arr[0].length) {
tmpobj = {}
arr.forEach((subarr, index) => {
tmpobj = {
...tmpobj,
...subarr[counter]
}
})
finalarr.push(tmpobj)
counter++;
}
console.log('Final array:', finalarr)
I am trying to transform a nested structure, using the library of lodash, I have achieved the expected result, but they are not functional if the structure changes, so I come to you to help me make more robust the function that transforms the JSON.
the initial structure looks like this
const data = {
foo: {
bar: {
baz: [{ a: 1, b: 2, c: 3 }]
},
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
},
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
bar: {
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
baz: {
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
}
};
after being transformed
const transform = [
{
name: 'barfoo',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'bazfoo',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'foofoo',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'bazbar',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'foobaz',
results: [{ a: 1, b: 2, c: 3 }]
}
];
The idea of the transformation is to join the nested key of the first level with the key of the parent node to generate the value of name in the new object and the value of the object in the 2 level as the value of results
for example for the first iteration of foo object in data
name = key(foo.bar) + key(foo)
results = value(foo.bar.baz)
name = 'barfoo'
results = [{ a: 1, b: 2, c: 3 }]
name = key(foo.baz) + key(foo)
results = value(foo.baz.bar)
name = 'bazfoo'
results = [{ a: 1, b: 2, c: 3 }]
name = key(foo.foo) + key(foo)
results = value(foo.foo.bar)
name = 'foofoo'
results = [{ a: 1, b: 2, c: 3 }]
and so with the other objects that are inside data.
I'm not sure if the structure will ever vary, but I added a few extra test cases so you can see how this will behave in some additional scenarios.
const data = {
foo: {
bar: {
baz: [{ a: 1, b: 2, c: 3 }]
},
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
},
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
bar: {
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
baz: {
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
a1: {
a2: [{ a: 1, b: 2, c: 3 }]
},
b1: [{ a: 1, b: 2, c: 3 }],
c1: {
c2: {
c3: {
c4: [{ a: 1, b: 2, c: 3 }]
}
},
c5: [{ a: 1, b: 2, c: 3 }]
},
d1: {
d2: {
d3: undefined
}
},
e1: {
e2: {
e3: null
}
},
f1: {
f2: {
// Ignored
}
}
};
function transformObject(object, name) {
if (!name) {
name = "";
}
return _.flatten(_.map(object, function(value, key) {
if (typeof value === "undefined"
|| value === null
|| _.isArray(value)) {
return {
name: name,
results: value
}
}
var objectName = key + name;
return transformObject(value, objectName);
}));
}
transformObject(data);
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"));