I have a sample Array of objects like this
let items = [
{
a: '',
b: 2,
c: 3
},
{
a: '',
b: '',
c: 5,
d: 10
},
{
a: '',
b: '',
c: 6,
}
]
I want to find the first object that has the highest number of keys.
Clearly from the above, the second object has the highest number of keys.
How can I achieve this?
Thank you.
Something like this will work:
let highestLength = 0;
let highestItem = 0;
for (let i = 0; i < items.length; i++) {
let objLength = Object.keys(items[i]).length;
if (objLength > highestLength) {
highestLength = objLength;
highestItem = i;
}
}
Then highestItem will hold the index of the first element with the highest number of keys.
Start by .sort()ing in descending order by number of keys. Then return the first element.
let items = [{
a: '',
b: 2,
c: 3
},
{
a: '',
b: '',
c: 5,
d: 10
},
{
a: '',
b: '',
c: 6,
}
];
const mostkeys = items.sort(
(a,b) => Object.keys(b).length - Object.keys(a).length
)[0];
console.log( mostkeys );
If, however, you have more than one having the most keys and you would like to return all, your approach would be slightly different at the last step:
let items = [{
a: '',
b: 2,
c: 3
},
{
a: '',
b: '',
c: 5,
d: 10
},
{
a: '',
b: '',
c: 6,
},
{
c: '',
d: '',
e: 6,
f: 3
}
];
const mostkeys = items.sort(
(a,b) => Object.keys(b).length - Object.keys(a).length
)
.reduce((m, cur, i, a) =>
i === 0 ? [...m,cur] : Object.keys(cur).length < Object.keys(m[0]).length ? m : [...m,cur], []
);
console.log( mostkeys );
Related
I'm trying to reduce an array, and transform it in multiple array.
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 }
];
var newArray = array.reduce(
(memo, curr) => {
memo.forEach((item, key) => {
const found = item.filter((el) => el.a === curr.a && el.b === curr.b);
if (found.length > 0) return memo[key].push(curr);
else return memo.push([curr]);
});
return memo;
},
[[]]
);
The needed result I try to get is
[
[
{ a: 1, b: 5 },
{ a: 1, b: 5 }
],
[
{ a: 1, b: 6 },
{ a: 1, b: 6 },
],
[
{ a: 1, b: 4 },
]
];
But as you can see if you try, because I push on the memo, the loop continue to fire. And the result contain hundreds arrays.
How I'm supposed to do to limit this loop and get the right result ?
Thanks a lot in advance :)
You could use Map to group the element by the key of {a, b}, and then get the values of the group
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 },
];
var newArray = Array.from(
array
.reduce((map, curr) => {
const key = JSON.stringify({ a: curr.a, b: curr.b });
if (!map.has(key)) {
map.set(key, []);
}
map.get(key).push(curr);
return map;
}, new Map())
.values()
);
console.log(newArray);
Look at your code. You have a triple nested loop, which is insane and definitely not needed to achieve this. Why not use a map?
Here is a function that will do what you want to do with any array of objects given.
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 },
];
const separate = (arr) => {
const reduced = arr.reduce((acc, curr) => {
const path = JSON.stringify(curr);
if (!acc[path]) acc[path] = [];
acc[path].push(curr);
return acc;
}, {});
return Object.values(reduced);
};
console.log(separate(array));
If you push inside for loop it will going to push for every reduce function iteration also.
you can achieve by adding some local variables like here
const array = [
{ a: 1, b: 6 },
{ a: 1, b: 5 },
{ a: 1, b: 6 },
{ a: 1, b: 4 },
{ a: 1, b: 5 }
];
// shift changes the orginal array
// it will remove and return firstElement
var firstElement = array.shift(1);
var newArray = array.reduce(
(memo, curr) => {
let isFound = false;
let index = 0;
memo.forEach((item, key) => {
const found = item.filter((el) => el.a === curr.a && el.b === curr.b);
if(found.length > 0){
index = key;
isFound = true;
return;
}
});
if(isFound) {
memo[index].push(curr);
} else {
memo.push([curr]);
}
return memo;
},
[[firstElement]]
);
console.log(newArray);
Say I have an array of objects like this:
const arr = [
{ a: 1, b: 2, c: 3, d: 4 },
{ a: 1 },
{ a: 1, b: 2, c: 3 },
{ a: 1, b: 2 }
];
How can I return the object with the most properties/keys? Preferably using in an efficient and terse manner using higher order functions.
You could assign to a single object.
const
array = [{ a: 1, b: 2, c: 3, d: 4 }, { a: 1 }, { a: 1, b: 2, c: 3 }, { a: 1, b: 2 }],
object = Object.assign({}, ...array);
console.log(object);
If you have different values, you could reduce the array.
const
array = [{ a: 1, b: 2, c: 3, d: 4 }, { a: 1 }, { a: 1, b: 2, c: 3 }, { a: 1, b: 2 }],
object = array.reduce((a, b) => Object.keys(a).length > Object.keys(b).length
? a
: b
);
console.log(object);
You can get the number of keys from an object by calling Object.keys(obj) and then checking it's length property.
With that, you could reduce the array by checking each pair of objects and return the one with the most keys as a one liner:
const biggestObject =
arr.reduce((a, b) => Object.keys(a).length > Object.keys(b).length ? a : b);
I'm parsing a csv fils to json with node-csvtojson and I got a JSONarray with the following code
csv({delimiter: ';'}).fromFile(path).then((jsonObj)=>{
data = jsonObj;
console.log(jsonObj);
})
with a csv like
a,b,c
A,B,C
1,2,3
1,B,C
I have got
[
{
a: A,
b: B,
c: C,
},
{
a: 1,
b: 2,
c: 3,
},
{
a: 1,
b: B,
c: C
}
]
But I want to find every object who has the element a === 1 and I want to have all the content of the object,
like this:
{
a: 1,
b: 2,
c: 3,
},
{
a: 1,
b: B,
c: C,
}
But I 'm struggling to do that, I have tried with array.filter but without success then I have tried to do this with array.map but I got lost on how to do.
Do you have any idea on or I could do that ?
Than you
Use Array.filter like so:
const data = [{
a: 'A',
b: 'B',
c: 'C',
},
{
a: 1,
b: 2,
c: 3,
},
{
a: 1,
b: 'B',
c: 'C'
}
];
console.log(data.filter(({ a }) => a == 1));
If you want this to work with old browsers, here's an ES5-compliant version:
var data = [{
a: 'A',
b: 'B',
c: 'C',
},
{
a: 1,
b: 2,
c: 3,
},
{
a: 1,
b: 'B',
c: 'C'
}
];
console.log(data.filter(function(obj) {
return obj.a == 1
}));
Simple use Array.filter to filter through the object array and select the one having property a === 1
var arr = [{"a":"A","b":"B","c":"C"},{"a":1,"b":2,"c":3},{"a":1,"b":"B","c":"C"}];
const filteredArr = arr.filter(obj => obj.a === 1);
console.log(filteredArr);
Using Array.reduce you can do the same thing:
var arr = [{"a":"A","b":"B","c":"C"},{"a":1,"b":2,"c":3},{"a":1,"b":"B","c":"C"}];
const redArr = arr.reduce((acc, obj) => {
return acc = obj.a === 1 ? acc.concat(obj) : acc;
}, []);
console.log(redArr);
Using Array.map for this problem is not the right approach, although it is possible:
var arr = [{"a":"A","b":"B","c":"C"},{"a":1,"b":2,"c":3},{"a":1,"b":"B","c":"C"}];
const mapArr = arr.map(obj => obj.a === 1 ? obj : undefined).filter(obj => obj); //hack to remove undefined elements
console.log(mapArr);
console.log([{
a: 'A',
b: 'B',
c: 'C',
},
{
a: 1,
b: 2,
c: 3,
},
{
a: 1,
b: 'B',
c: 'C'
}
].filter(o => o.a === 1))
Try this :
var arr = [{"a":"A","b":"B","c":"C"},{"a":1,"b":2,"c":3},{"a":1,"b":"B","c":"C"}];
var res = arr.filter(obj => obj.a === 1);
console.log(res);
I have an array of objects as such:
var data = [{ a: 1 },
{ a: 1 },
{ a: 2 },
{ a: 2 }];
How can I create, with lodash, a new array just like data but for which we added a new property b: 1 to each object that has the property a equal to 1?
The new array should be like this:
var newdata = [{ a: 1, b: 1 },
{ a: 1, b: 1 },
{ a: 2 },
{ a: 2 }];
I guess we could combine both _.assign and _.filer but I'm not sure how.
You can do this with lodash via either _.defaults or _.assign / _.assignIn / _.extend:
var data = [{ a: 1 },{ a: 1 },{ a: 2 },{ a: 2 }];
console.log(_.map(data, x => x.a==1 ? _.defaults(x, {b: 1}) : x))
console.log(_.map(data, x => x.a==1 ? _.assign(x, {b: 1}) : x))
console.log(_.map(data, x => x.a==1 ? _.assignIn(x, {b: 1}) : x))
console.log(_.map(data, x => x.a==1 ? _.extend(x, {b: 1}) : x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
You could map the array and use a new property if the condition is true.
var data = [{ a: 1 }, { a: 1 }, { a: 2 }, { a: 2 }],
newData = data.map(o => Object.assign({}, o, o.a === 1 && { b: 2 }));
console.log(newData);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you're happy with vanilla Javascript, this here works:
var data = [{ a: 1 },
{ a: 1 },
{ a: 2 },
{ a: 2 }];
var newData = [];
newData = data.reduce((acc, el) => {
return el.a === 1 ? acc.concat({a: el.a, b: 1}) : acc.concat(el);
}, []);
console.log(newData);
// expected output: [{a: 1, b: 1},
// {a: 1, b: 1},
// {a: 2},
// {a: 2}]
You can generate a function with lodash by applying _.cond() to _.map() with _.partialRight.
Use _.matches() to detect if the object has a: 1 property, and _.assign() { b: 1 } if it does.
When a is not 1, use _.stubTrue() to always return the original object via _.identity():
var func = _.partialRight(_.map, _.cond([
[_.matches({ 'a': 1 }), o => _.assign({}, o, { b: 2 })],
[_.stubTrue, _.identity]
]));
var data = [{ a: 1 },{ a: 1 },{ a: 2 },{ a: 2 }];
const result = func(data);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
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"));