How to loop object have many array object like that - javascript

I have object 1 like that
const object1 = {
Sheet1: [
{ A: '', B: '', C: '', D: '', E: '' },
{
A: 'Cash Report - Processed',
B: 'Store: 54101 Date: 1/30/2020',
D: '',
E: ''
},
{ A: '', B: '', C: '', D: 'Adj: 00', E: 'Adj: 01' },
],
Sheet2: [ { D: 'a' }, { G: 's' }, { G: 'b' } ]
};
I want delete blank data but I have issue when I loop object 1, I can't get keym my code
Object.keys(object1).forEach(function(key) {
console.log(key, object1[key]);
});
I want data will like that
{
Sheet1: [
{
A: 'Cash Report - Processed',
B: 'Store: 54101 Date: 1/30/2020',
},
{ D: 'Adj: 00', E: 'Adj: 01' },
],
Sheet2: [ { D: 'a' }, { G: 's' }, { G: 'b' } ]
};

You can accomplish this using Object.entries and Array.reduce.
const input = {
Sheet1: [
{ A: '', B: '', C: '', D: '', E: '' },
{
A: 'Cash Report - Processed',
B: 'Store: 54101 Date: 1/30/2020',
D: '',
E: ''
},
{ A: '', B: '', C: '', D: 'Adj: 00', E: 'Adj: 01' },
],
Sheet2: [ { D: 'a' }, { G: 's' }, { G: 'b' } ]
};
const output = Object.entries(input).reduce((acc, cur) => {
acc[cur[0]] = cur[1].reduce((curAcc, curVal) => {
const notNull = Object.entries(curVal).filter(([ key, val ]) => val);
if (notNull.length > 0) {
curAcc.push(Object.fromEntries(notNull));
}
return curAcc;
}, []);
return acc;
}, {});
console.log(output);

Use this:
for(const key in object1){
const element = object1[key];
object1[key] = element.map(x => {
for(const prop in x){
if(!x[prop]){
delete x[prop];
}
}
return Object.keys(x).length ? x : null
}).filter(x => !!x);
};

Use Object.entries to transform object to array of key-value pairs, manipulate that pairs, remove pairs with empty value, then transform back to object using Object.fromEntries
const object1 = {
Sheet1: [
{ A: "", B: "", C: "", D: "", E: "" },
{
A: "Cash Report - Processed",
B: "Store: 54101 Date: 1/30/2020",
D: "",
E: "",
},
{ A: "", B: "", C: "", D: "Adj: 00", E: "Adj: 01" },
],
Sheet2: [{ D: "a" }, { G: "s" }, { G: "b" }],
}
const notEmptyObject = (obj) => Object.keys(obj).length > 0
const removeEmptyValues = (obj) =>
Object.fromEntries(Object.entries(obj).filter(([_, value]) => Boolean(value)))
const res = Object.fromEntries(
Object.entries(object1).map(([sheet, data]) => [
sheet,
data
.map(removeEmptyValues)
.filter(notEmptyObject),
])
)
console.log(res)

Related

How to elegantly merge multiple objects with overlapping keys?

Let's consider multiple objects with overlapping keys, where each key indicates a week of the year and the values are objects of integer test results, like
const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } };
const cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } };
const xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } };
What is an elegant way to merge them to a single object that contains all weeks as keys and the values as merged-objects, such that:
const merged_tests = {
week1: { a: 4, b: 6, x: 1, y: 1 },
week2: { a: 0, b: 9, c: 2, d: 5 },
week3: { c: 6, d: 7 },
week4: { x: 100, y: 123 },
};
Using Array#reduce, iterate over the objects while updating the final one (accumulator)
In each iteration, using Object#entries and Array#forEach, iterate over the pairs of the current object and update the final one
const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } };
const cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } };
const xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } };
const merged = [ab_tests, cd_tests, xy_tests].reduce((merged, current) => {
Object.entries(current).forEach(([key, value]) => {
merged[key] ??= {};
merged[key] = { ...merged[key], ...value };
});
return merged;
}, {});
console.log(merged);
You could loop through the key of each object and update an output object with the same key
const inputs = [ab_tests, cd_tests, xy_tests],
output = { }
for (const o of inputs) {
for (const key in o)
Object.assign(output[key] ??= {}, o[key])
}
Here's a snippet:
const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } },
cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } },
xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } },
inputs = [ab_tests, cd_tests, xy_tests],
output = {}
for (const o of inputs) {
for (const key in o)
Object.assign(output[key] ??= {}, o[key])
}
console.log(output)
flatMap(Object.entries) flattens things so there's only one loop to read, making it a little more readable in my opinion.
function merge(...tests) {
return tests.flatMap(Object.entries).reduce(
(obj, [k, v]) => Object.assign(obj, {[k]: {...obj[k], ...v}}), {});
}
console.log(merge(ab_tests, cd_tests, xy_tests));
You could reduce the array of objects by grouping with the keys of the outer objects.
const
merge = array => array.reduce((r, o) => Object
.entries(o)
.reduce((t, [k, q]) => {
Object.assign(t[k] ??= {}, q);
return t;
}, r),
{}),
ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } },
cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } },
xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } },
result = merge([ab_tests, cd_tests, xy_tests]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Merge objects in a loop

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)

Pick specific properties from nested objects

I have javascript objects which looks similar to this:
{
id: 43,
name: 'ajajaj'
nestedObj1: {
id: 53,
name: 'ababab'
foo: 'xxxx'
nestedObj2: {
id: 90,
name: 'akakaka'
foo2: 'sdsddd'
surname: 'sdadasd'
nestedObj3: {
id: ..
name: ..
...
},
objectsArr: [
{id: .., name: ..., nestedOb4: {...} },
{id: .., name: ..., nestedOb4: {...} }
]
},
foo0: ...
}
name: 'blabla'
}
Each objects which I have, has property id and name. My goal is pick all properties in first level, so properties of object with id 43 I pick all, and from every nested objects, I just pick properties id and name, and another will be thrown away.
I am using in project lodash, so I found function pick, but I don't know how to use this function for nested objects. Also note that nested objects could be also in array. Exist some elegant way for that please? Thanks in advice.
pick function of lodash is able to fetch nested property like so:
_.pick(obj, [
'id',
'name',
'nestedObj1.id',
'nestedObj1.name',
'nestedObj1.nestedObj2.id',
'nestedObj1.nestedObj2.name',
'nestedObj1.nestedObj2.nestedObj3.id',
'nestedObj1.nestedObj2.nestedObj3.name',
'nestedObj1.nestedObj2.objectsArr[0].id',
'nestedObj1.nestedObj2.objectsArr[0].name',
'nestedObj1.nestedObj2.objectsArr[0].nestedObj4.id',
'nestedObj1.nestedObj2.objectsArr[0].nestedObj4.name',
'nestedObj1.nestedObj2.objectsArr[1].id',
'nestedObj1.nestedObj2.objectsArr[1].name',
'nestedObj1.nestedObj2.objectsArr[1].nestedObj4.id',
'nestedObj1.nestedObj2.objectsArr[1].nestedObj4.name',
])
The problem is, you must know the array length to pick all objects in them.
To mitigate this, you can use this function to iterate over arrays:
const _ = require('lodash')
const pickExtended = (object, paths) => {
return paths.reduce((result, path) => {
if (path.includes("[].")) {
const [collectionPath, itemPath] = path.split(/\[]\.(.+)/);
const collection = _.get(object, collectionPath);
if (!_.isArray(collection)) {
return result;
}
const partialResult = {};
_.set(
partialResult,
collectionPath,
_.map(collection, item => pickExtended(item, [itemPath]))
);
return _.merge(result, partialResult);
}
return _.merge(result, _.pick(object, [path]));
}, {});
};
You can then get the result you want with:
pickExtended(obj, [
'id',
'name',
'nestedObj1.id',
'nestedObj1.name',
'nestedObj1.nestedObj2.id',
'nestedObj1.nestedObj2.name',
'nestedObj1.nestedObj2.nestedObj3.id',
'nestedObj1.nestedObj2.nestedObj3.name',
'nestedObj1.nestedObj2.objectsArr[].id',
'nestedObj1.nestedObj2.objectsArr[].name',
'nestedObj1.nestedObj2.objectsArr[].nestedObj4.id',
'nestedObj1.nestedObj2.objectsArr[].nestedObj4.name',
])
Here is the associated mocha test:
describe("pickExtended", () => {
const object = {
a: 1,
b: "2",
c: { d: 3, e: 4 },
f: [
{ a: 11, b: 12, c: [{ d: 21, e: 22 }, { d: 31, e: 32 }] },
{ a: 13, b: 14, c: [{ d: 23, e: 24 }, { d: 33, e: 34 }] }
],
g: [],
h: [{ a: 41 }, { a: 42 }, { a: 43 }]
};
it("should pick properties in nested collection", () => {
expect(
pickExtended(object, ["a", "c.d", "f[].c[].d", "g[].a", "b[].a", "h[]"])
).to.deep.equal({
a: 1,
c: { d: 3 },
f: [{ c: [{ d: 21 }, { d: 31 }] }, { c: [{ d: 23 }, { d: 33 }] }],
g: []
});
});
});
Keep in mind that no performance test was done on this. It might get slow for big object.
You can do this using recursion, as long as your objects do have an end to the levels of nesting. Otherwise, you run the risk of causing a stack limit error.
function pickProperties(obj) {
var retObj = {};
Object.keys(obj).forEach(function (key) {
if (key === 'id' || key === 'name') {
retObj[key] = obj[key];
} else if (_.isObject(obj[key])) {
retObj[key] = pickProperties(obj[key]);
} else if (_.isArray(obj[key])) {
retObj[key] = obj[key].map(function(arrayObj) {
return pickProperties(arrayObj);
});
}
});
return retObj;
}

How to assign the property if null

I had an array of objects with duplicated ids. Each object associated with the id has a property A and property B. For each loop, first index array's property A has value, but property B is null. In second index array, property A null, property B has value. The first two index will merge according to the same id. The output doesn't produce values for both property A and property B, but either one still null.
Array
{
id: 123,
A: “value A1”
B: null
},
{
id: 123,
A: null,
B: “value b”
},
{
id: 123,
A: “value A2”
B: null
},
{
id: 456,
A: "a2 value",
B: "b2 value"
}
Code
var output = _.groupBy(arr, function(o){
return o.id;
})
Output
{
id: 123,
A: [“value A1”,“value A2”]
B: null
},
{
id: 456,
A: ["a2 value"],
B: "b2 value"
}
Expected
{
id: 123,
A: [“value A1”,“value A2”]
B: “value b”
},
{
id: 456,
A: ["a2 value"],
B: "b2 value"
}
You can do it without underscore and loadash:
var arr = [
{
id: 123,
A: "value A1",
B: null
},
{
id: 123,
A: null,
B: "value b"
},
{
id: 123,
A: "value A2",
B: null
},
{
id: 456,
A: "a2 value",
B: "b2 value"
}
];
var arr2 = arr.reduce((m, o) => {
var sameObj = m.find(it => it.id === o.id);
if (sameObj) {
o.A && (sameObj.A.push(o.A));
sameObj.B = sameObj.B || o.B;
} else {
o.A = o.A ? [o.A] : [];
m.push(o);
}
return m;
}, []);
console.log(arr2);
Following is without underscore or lodash. Just with vanilla JavaScript.
var data = [{
id: 123,
A: "some value",
B: null
},
{
id: 123,
A: null,
B: "b value"
},
{
id: 456,
A: "a2 value",
B: "b2 value"
}
];
var outputObject = {},
outputArray = [];
data.forEach(function(obj) {
if (!outputObject[obj.id]) {
outputObject[obj.id] = obj;
} else {
if (obj.B !== null) {
outputObject[obj.id].B = obj.B;
}
if (obj.A !== null) {
outputObject[obj.id].A = obj.A;
}
}
});
//Convert to an array
Object.keys(outputObject).forEach(function(key) {
outputArray.push(outputObject[key]);
});
console.log(outputArray);

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