Get sum of property values within array of objects - javascript

I am trying to get an object as output using JavaScript reduce function. It's working, if I define an object {ageTotal: 0} as the second argument. How can I implement this sum of age without defining a second argument property only using an empty object {}.
const users = [
{ name: 'Tyler', age: 28},
{ name: 'Mikenzi', age: 26},
{ name: 'Blaine', age: 30 }
];
// output as a *int*
const sumAge = users.reduce((totals, current) => {
return totals + current.age;
}, 0);
console.log(sumAge);
// output as *object*
function getUserData (users) {
return users.reduce((data, user) => {
data.ageTotal += user.age
return data;
}, { ageTotal: 0 });
}
console.log(getUserData(users));

You may use short-circuit evaluation while incrementing to add current age value to accumulator property ageTotal (if it exists, or set it to 0, otherwise):
data.ageTotal = (data.ageTotal || 0) + user.age
Following is a quick demo:
const users = [
{ name: 'Tyler', age: 28},
{ name: 'Mikenzi', age: 26},
{ name: 'Blaine', age: 30 }
];
// output as *object*
function getUserData (users) {
return users.reduce((data, user) => {
data.ageTotal = (data.ageTotal || 0) + user.age
return data;
}, {});
}
console.log(getUserData(users));
Or, if you seek to make your syntax more concise:
const users = [
{ name: 'Tyler', age: 28},
{ name: 'Mikenzi', age: 26},
{ name: 'Blaine', age: 30 }
];
// output as *object*
const getUserData = users =>
users.reduce((data, {age}) =>
(data.ageTotal = (data.ageTotal || 0) + age, data), {});
console.log(getUserData(users));

Related

Make the Javascript includes() method return items with hyphen

I am using the javascript includes() method to filter an array of object that looks like this;
const names = [
{name: 'Rose-mary Thompson', age: 30},
{name: 'Rose mary Samuel', age: 30},
{name: 'John-Doe Jackson', age: 30},
{name: 'John Doe Philips', age: 30},
]
if I search for 'Rose mary', I want the result to also include 'Rose-mary'.
I don't want the user to have to type hyphen(-) before the names with - come out in the search result.
This is my code;
let searchQuery = 'Rose mary'
let filteredResults = []
const searchItems = () => {
if (searchQuery.length > 1) {
const results = names.filter((name) => {
return name.name.toLowerCase().includes(searchQuery.toLowerCase());
});
filteredResults = results
}
}
Please how do I achieve this?
includes will only find an exact match. Since you're already adjusting each string as you search, you could "normalize" both searchQuery and name to be lower case with - converted to space:
const searchItems = () => {
if (searchQuery.length > 1) {
const adjustedQuery = searchQuery.toLowerCase().replace(/-/g, " ");
const results = names.filter((name) => {
return name.name
.toLowerCase()
.replace(/-/g, " ")
.includes(adjustedQuery);
});
filteredResults = results;
}
};
const names = [
{ name: "Rose-mary Thompson", age: 30 },
{ name: "Rose mary Samuel", age: 30 },
{ name: "John-Doe Jackson", age: 30 },
{ name: "John Doe Philips", age: 30 },
];
let searchQuery = "Rose mary";
let filteredResults = [];
const searchItems = () => {
if (searchQuery.length > 1) {
const adjustedQuery = searchQuery.toLowerCase().replace(/-/g, " ");
const results = names.filter((name) => {
return name.name
.toLowerCase()
.replace(/-/g, " ")
.includes(adjustedQuery);
});
filteredResults = results;
}
};
searchItems();
console.log(filteredResults);
Side note: I'd also make names and searchQuery parameters to the function, and have it return the results:
const names = [
{ name: "Rose-mary Thompson", age: 30 },
{ name: "Rose mary Samuel", age: 30 },
{ name: "John-Doe Jackson", age: 30 },
{ name: "John Doe Philips", age: 30 },
];
const searchItems = (names, searchQuery) => {
if (searchQuery.length > 1) {
const adjustedQuery = searchQuery.toLowerCase().replace(/-/g, " ");
const results = names.filter((name) => {
return name.name
.toLowerCase()
.replace(/-/g, " ")
.includes(adjustedQuery);
});
return results;
}
return names; // Or whatever you want when there's no query
};
console.log(searchItems(names, "Rose mary"));

How can I minimize a code function that finds repeated values in an array of objects in JavaScript?

I need to fix this function, which must find two similar names in an array of objects.
I tried to do this, and it's working, but the test tells me that it should be just only one loop and one if
function searchByName() {
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
for (let obj of values) {
for (let elem of values) {
if (obj == elem)
continue;
if (elem.name === obj.name && elem.age === obj.age) {
console.log(obj);
break;
}
}
}
};
Here is the example that must come out:
[
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Andrew', birthDate: '2011-05-09' }
]
Just push names to the array and stop when there is a duplicate:
let hasDuplicates = [];
values.forEach((e, idx) => {
if(!hasDuplicates.includes(e.name))
if(idx !== values.length-1) { hasDuplicates.push(e.name); }
else { hasDuplicates = false; }
else { hasDuplicates = e; }
});
And then you can use that variable:
if(hasDuplicates) {...}
You can find the duplication count by name key using Array.reduce function.
And from the duplication result, you can filter the duplicated ones only and show them using Array.filter & Array.map.
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
let duplicates = values.reduce((acc, cur) => {
if (acc[cur.name]) {
acc[cur.name].push(cur.birthDate);
} else {
acc[cur.name] = [ cur.birthDate ];
}
return acc;
}, {});
duplicates = Object.entries(duplicates).filter(([name, birth]) => birth.length > 1).map(([name, birth]) => {
return birth.map((item) => ({
name,
birthDate: item
}));
});
console.log(duplicates);
You can use the reduce method to iterate over the array and push items into the array. Then we just filter the array by its length. So if the length is greater than 1, it means we have found a duplicate:
const result = values.reduce((a, c)=> {
a[c.name] = a[c.name] || {...c, items: []} ;
a[c.name].items.push(c);
return a;
}, { });
let result = Object.values(result).filter(f => f.items.length > 1).flatMap(s => s.items);
An example:
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
const result = values.reduce((a, c)=> {
a[c.name] = a[c.name] || {...c, items: []} ;
a[c.name].items.push(c);
return a;
}, { });
console.log(Object.values(result).filter(f => f.items.length > 1).flatMap(s => s.items));

How to find duplicates from list of array in angular 6 using some?

Hi I have list of values in array as bellow
var users = [{
name: 'John',
email: 'johnson#mail.com',
age: 25,
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
},
{
name: 'John',
email: 'johnson#mail.com',
age: 25,
}];
I should find duplicates row from the above array (need to compare all the fields name, email, and age)
I have used some function to find a duplicate value as below but need to pass multiple conditions in it. How to do that
const unique = new Set();
const showError = this.users.some(element => unique.size === unique.add(element.name).size);
As I have passed the name I need to verify email and age. How to do that
Maintain counter while checking equality, as every object will be equal with same object, hence check if counter is greater than 1 for getting status.
const status = users.some(user => {
let counter = 0;
for (const iterator of users) {
if (iterator.name === user.name && iterator.email === user.email && iterator.age === user.age) {
counter += 1;
}
}
return counter > 1;
});
var users = [
{
name: 'John',
email: 'johnson#mail.com',
age: 25,
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
},
{
name: 'John',
email: 'johnson#mail.com',
age: 25,
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
},,
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
},
{
name: 'Harry',
email: 'harry#mail.com',
age: 23,
},
{
name: 'Kane',
email: 'kane#mail.com',
age: 65,
},
{
name: 'Ron',
email: 'ron#mail.com',
age: 65,
},
{
name: 'Ron',
email: 'ron#mail.com',
age: 65,
}
];
// complexity of this function is n where n is the no of users
var data = uniqueData(users, 'email');
console.log(data)
function uniqueData(array, key) {
// create new objects for use
var uniqueArray = [];
var map = new Map();
// loop throught array
array.forEach((user,index) => {
// first item is always unique add to unique whithout check
if(index == 0) {
// using map set first item in map key and value is dynamic which we can set
map.set(array[index].email, array[index].email);
uniqueArray.push(array[index]);
}
//check if the key already exists if exists do not push else push
if (!map.get(user[key])) {
map.set(user[key], user[key]);
uniqueArray.push(user);
}
});
return uniqueArray;
}
Use below code for remove duplicates
function removeDuplicates(array, key) {
let lookup = {};
return array.filter(obj => !lookup[obj[key]] && lookup[obj[key]] = true);
}
Try this:
const showError: boolean = Array.from(new Set(users.map(user => JSON.stringify(user)))).length != users.length;
var frequency = users.reduce(function(seen, currentItem) {
if (currentItem in seen) {
seen[currentItem] = seen[currentItem] + 1;
} else {
seen[currentItem] = 1;
}
return seen;
}, {});
for (var key in frequency) {
if (frequency[key] > 1) {
result.push(key.split(",").map(function(currentItem) {
return parseInt(currentItem);
}));
}
}
console.log(result);
hope this will help you

How to calculate the average in JavaScript of the given properties in the array of objects

I have an array of objects. Each object contains a few properties and one of these properties is age.
var people = [
{
name: 'Anna',
age: 22
},
{
name: 'Tom',
age: 34
}, {
name: 'John',
age: 12
},
]
I want to calculate the average for all the age properties in this array. I use this function:
let getAverage = arr => {
let reducer = (total, currentValue) => total + currentValue;
let sum = arr.reduce(reducer)
return sum / arr.length;
}
It works fine for the simple array like [22, 34, 12] but it does not for the arrays of objects.
How to modify my getAverage function to make it works also with arrays of object?
Here is the snippet with my code:
https://jsfiddle.net/marektchas/kc8Ls0f5/2/
You don't need to modify getAverage - you can pre-process the array before handing it off, in which case getAverage will work exactly as needed:
var people = [
{
name: 'Anna',
age: 22
},
{
name: 'Tom',
age: 34
}, {
name: 'John',
age: 12
},
]
let getAverage = arr => {
let reducer = (total, currentValue) => total + currentValue;
let sum = arr.reduce(reducer)
return sum / arr.length;
}
let ages = people.map(person => person.age);
console.log(getAverage(ages));
If you want to change getAverage, so it can handle any potential type of input, then you can add an optional argument that will perform value extraction, so you don't have to run a map on each array.
var numbers = [
1,
2,
3,
4,
5
]
var people = [
{
name: 'Anna',
age: 22
},
{
name: 'Tom',
age: 34
}, {
name: 'John',
age: 12
},
]
var moreComplexObjects = [
{
a: {
b: {
c: 6
}
}
},
{
a: {
b: {
c: 7
}
}
},
{
a: {
b: {
c: 8
}
}
}
]
//the default value of the second parameter is a function that returns the input
let getAverage = (arr, valueExtractor = x => x) => {
//extract the value of each element
let reducer = (total, currentValue) => total + valueExtractor(currentValue);
let sum = arr.reduce(reducer, 0) //make sure you add an initialiser
return sum / arr.length;
}
const ageExtractor = person => person.age;
const complexKeyExtractor = obj => obj.a.b.c;
console.log(getAverage(numbers));
console.log(getAverage(people, ageExtractor));
console.log(getAverage(moreComplexObjects, complexKeyExtractor));
A note for the above, if you don't supply a second parameter to Array#reduce, then the first time it runs total will be the first element of the array, however the second time and onwards, it will be the sum so far. It's not worth handling that case, so supplying an initial value solves it.
You can use reduce() and add property age of each object in array to ac. Don't forget to pass 0(initial value of ac) as second argument otherwise it would return NaN
var people = [
{
name: 'Anna',
age: 22
},
{
name: 'Tom',
age: 34
}, {
name: 'John',
age: 12
},
]
let avgs = people.reduce((ac,a) => a.age + ac,0)/people.length
console.log(avgs)
You could supply a function for the wanted property and reduce with a start value.
const
getAverage = (array, fn = v => v) => {
var reducer = fn => (total, item) => total + fn(item),
sum = array.reduce(reducer(fn), 0);
return sum / array.length;
};
var people = [{ name: 'Anna', age: 22 }, { name: 'Tom', age: 34 }, { name: 'John', age: 12 }];
console.log(getAverage(people, o => o.age));
We can use Array.reduce to compute the sum, where acc is the accumulated sum and the individual objects are destructured to the age variable then calculate the average by dividing the sum with the array length:
var people = [
{
name: 'Anna',
age: 22
},
{
name: 'Tom',
age: 34
}, {
name: 'John',
age: 12
},
];
function getAvg(arr){
return (people.reduce((acc, {age}) => (acc + age), 0))/arr.length;
}
console.log(getAvg(people));
We can also map the function to the age property and calculate the sum by joining the array into a string and evaluating it inside the Function constructor:
var people = [
{
name: 'Anna',
age: 22
},
{
name: 'Tom',
age: 34
}, {
name: 'John',
age: 12
},
];
function getAvg(arr){
return (new Function(`return ${people.map(({age}) => age).join("+")}`)()) / arr.length;
}
console.log(getAvg(people));
More Simplified code
just execute as getAverage(people.map(p=> p.age)); using your code
var people = [
{
name: 'Anna',
age: 22
},
{
name: 'Tom',
age: 34
}, {
name: 'John',
age: 12
},
];
let getAverage = arr => {
let reducer = (total, currentValue) => total + currentValue;
let sum = arr.reduce(reducer)
return sum / arr.length;
};
console.log(getAverage(people.map(p=> p.age)));

How to distinct an object based off multiple attributes

I am looking for a way to distinct an array of objects, the method needs to distinct by two attributes for instance,
let arr = [
{
name: "George",
surname: "Hendricks"
},
{
name: "George",
surname: "Marques"
},
{
name: "George",
surname: "Hendricks"
}
]
Once filtered should only return an array of 2 objects, George Hendricks and George Marques As they are unique. Currently I can only filter with ES6 Set like so
let uniArr = [...(new Set(arr))]
How can I accomplish my task as fast as possible (working with big data)
If the property values are really strings, you can combine them to make a unique key, and then build a new array using an object (or Set) to track the ones you've already seen. The advantage to using an object or Set is that you don't have to re-scan the array every time to find out if an entry is unique. Lookup time on them is typically much better (even dramatically better) than a linear search.
Here's an example with an object:
let arr = [
{
name: "George",
surname: "Hendricks"
},
{
name: "George",
surname: "Marques"
},
{
name: "George",
surname: "Hendricks"
},
];
let seen = Object.create(null);
let filtered = arr.filter(entry => {
const key = entry.name + "\u0000" + entry.surname;
// ^---- a string you know won't be in either name or surname
const keep = !seen[key];
if (keep) {
seen[key] = true;
}
return keep;
});
console.log(filtered);
Or with a Set:
let arr = [
{
name: "George",
surname: "Hendricks"
},
{
name: "George",
surname: "Marques"
},
{
name: "George",
surname: "Hendricks"
},
];
let seen = new Set();
let filtered = arr.filter(entry => {
const key = entry.name + "\u0000" + entry.surname;
// ^---- a string you know won't be in either name or surname
const keep = !seen.has(key);
if (keep) {
seen.add(key);
}
return keep;
});
console.log(filtered);
You can use Array.filter() method to filter the array, by searching over the couple name and surname.
This is how should be your code:
var filtered = arr.filter((person, index, selfArray) =>
index === selfArray.findIndex((p) => (
p.name === person.name && p.surname === person.surname
))
);
Demo:
let arr = [{
name: "George",
surname: "Hendricks"
},
{
name: "George",
surname: "Marques"
},
{
name: "George",
surname: "Hendricks"
},
];
var filtered = arr.filter((person, index, selfArray) =>
index === selfArray.findIndex((p) => (
p.name === person.name && p.surname === person.surname
))
);
console.log(filtered);

Categories

Resources