I have written a function which will return a name with the greatest number of children from a contact list (array of objects), but it is limited to the number of elements I include in the if statement. How can I make it go over all the elements in this contact list, regardless of size? I tried to make a for of loop, but it won't let me sort through elements using a counter "I", returning an error.
Thanks in advance!
let contacts = [
{
name: 'John',
children:
[
{ name: 'Mary', age: 11 }
]
}, {
name: 'Franklin',
children:
[
{ name: 'Susy', age: 7 },
{ name: 'Tom', age: 5 }
]
}
];
function p_w_m_children(arr){
let person_w_most_children = "";
arr.sort((elem_1, elem_2) => {
if (elem_1.children > elem_2.children){
person_w_most_children = elem_1.name;
}else{
person_w_most_children = elem_2.name;
}
});
return person_w_most_children;
}
console.log("The person who has most children is " + p_w_m_children(contacts)+ ".");
To find the person with the most children using arr.sort, you can use the following:
arr.sort((elem_1, elem_2) => {
return elem_1.children.length - elem_2.children.length
}
The key points being that you compare the .length of the arrays (rather than just comparing the arrays themselves) and that the .sort callback returns a value that can be sorted. The return is positive when elem_1.children.length > elem_2.children.length, negative when elem_1.children.length < elem_2.children.length and 0 when they are equal. This means the sort function can order the array correctly.
Then, once the array is sorted you can simply get the last element in the sorted array (the element with the largest value).
Edit for Clarification
let sortedArr = arr.sort((elem_1, elem_2) => {
return elem_1.children.length - elem_2.children.length;
};
return sortedArr[sortedArr.length - 1].name;
You can loop through the array and keep a record of the contact who has the most children. Replace that record with the current one in the loop if the number of children is greater.
Then return that contact when the loop is finished.
let contacts = [{
name: 'John',
children: [{
name: 'Mary',
age: 11
}]
}, {
name: 'Franklin',
children: [{
name: 'Susy',
age: 7
},
{
name: 'Tom',
age: 5
}
]
}];
function p_w_m_children(arr) {
let person_w_most_children;
contacts.forEach(contact => {
if (!person_w_most_children || contact.children.length > person_w_most_children.children.length) {
person_w_most_children = contact;
}
})
return person_w_most_children;
}
console.log("The person who has most children is " + p_w_m_children(contacts).name + ".");
Same as above without using forEach :
let contacts = [{
name: 'John',
children: [{
name: 'Mary',
age: 11
}]
}, {
name: 'Franklin',
children: [{
name: 'Susy',
age: 7
},
{
name: 'Tom',
age: 5
}
]
}];
function p_w_m_children(arr) {
let person_w_most_children;
for (let i = 0; i < contacts.length; i++) {
if (!person_w_most_children || contacts[i].children.length > person_w_most_children.children.length) {
person_w_most_children = contacts[i];
}
}
return person_w_most_children;
}
console.log("The person who has most children is " + p_w_m_children(contacts).name + ".");
Related
I have the following data structure:
persons: [
{ name: 'Joe', age: 20 },
{ name: 'Alex', age: 24 },
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
]
I want to get the oldest person-object for each existing name. So the result of this example would be:
filteredPersons: [
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
]
How can I achieve this? Note that the number of different names is not fixed.
You could take a Map and collect older ages for same names.
This soultion feature a function which compares two objects (or one object and a possible undefined) and if truthy and b.age is greater then a.age, it returns b, otherwise a.
At the end, only the values of the map are taken as result set.
const
older = (a, b) => b?.age > a.age ? b : a,
persons = [{ name: 'Joe', age: 20 }, { name: 'Alex', age: 24 }, { name: 'Joe', age: 34 }, { name: 'Bob', age: 19 }, { name: 'Alex', age: 56 }],
result = Array.from(persons.reduce((m, o) => m.set(
o.name,
older(o, m.get(o.name))
), new Map).values());
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
To do that in a single pass, you may employ Array.prototype.reduce() building up the Map that will have name as a key and store maximum age together with name as a value-object.
Once the Map is ready, you may extract its values with Map.prototype.values():
const src = [{name:'Joe',age:20},{name:'Alex',age:24},{name:'Joe',age:34},{name:'Bob',age:19},{name:'Alex',age:56},],
result = [...src
.reduce((acc, {name, age}) => {
const match = acc.get(name)
match ?
match.age = Math.max(age, match.age) :
acc.set(name, {name,age})
return acc
}, new Map)
.values()
]
console.log(result)
.as-console-wrapper{min-height:100%;}
Simply reduce the array and for each person in the array, check if the item has been encountered before, if so keep the oldest one, otherwise just keep the current object:
let results = persons.reduce((acc, person) => { // for each person in persons
if(!acc[person.name] || acc[person.name].age < person.age) { // if this person has never been encountered before (acc[person.name]) or if the already encountered one is younger (acc[person.name].age < person.age)
acc[person.name] = person; // store the current person under the name
}
return acc;
}, Object.create(null)); // Object.create(null) instead of {} to create a prototypeless object
This will return an object containing the oldest persons in this format { name: person, name: person, ... }. If you want to get them as an array, call Object.values like so:
let arrayResults = Object.values(results);
Demo:
let persons = [{ name: 'Joe', age: 20 }, { name: 'Alex', age: 24 }, { name: 'Joe', age: 34 }, { name: 'Bob', age: 19 }, { name: 'Alex', age: 56 }];
let results = persons.reduce((acc, person) => {
if(!acc[person.name] || acc[person.name].age < person.age) {
acc[person.name] = person;
}
return acc;
}, Object.create(null));
let arrayResults = Object.values(results);
console.log("results:", results);
console.log("arrayResults:", arrayResults);
Hope this is more understandable for you.
const persons = [
{ name: 'Joe', age: 20 },
{ name: 'Alex', age: 24 },
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
]
let personsObj = {}, mxPersons = []
persons.forEach(person => {
if (personsObj[person.name] == undefined) {
personsObj[person.name] = person.age
} else {
personsObj[person.name] = Math.max(person.age, personsObj[person.name])
}
})
for (const [key, value] of Object.entries(personsObj)) {
mxPersons.push({
name: key,
age: value
})
}
console.log(mxPersons)
The oldest people per name can be obtained by first grouping all people based on their name, then take the oldest person of each group.
This answer does introduce two helper functions groupBy and maxBy, which add some overhead but are really usefull in general.
const people = [
{ name: 'Joe', age: 20 },
{ name: 'Alex', age: 24 },
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
];
const oldestPeople = Array
.from(groupBy(people, person => person.name).values())
.map(people => maxBy(people, person => person.age));
console.log(oldestPeople);
function groupBy(iterable, fn) {
const groups = new Map();
for (const item of iterable) {
const key = fn(item);
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(item);
}
return groups;
}
function maxBy(iterable, fn) {
let max, maxValue;
for (const item of iterable) {
const itemValue = fn(item);
if (itemValue <= maxValue) continue;
[max, maxValue] = [item, itemValue];
}
return max;
}
Here's the code. I left the function block blank because I'm stumped
function getSummedAge(people) {
}
const examplePeopleArray = [
{ name: 'John', age: 10 },
{ name: 'Jack', age: 20 },
{ name: 'Jane', age: 25 }
];
console.log(getSummedAge(examplePeopleArray));
Checkout the Array.reduce method! It's purpose is to basically reduce an array into a single value, which I believe is what you're trying to do here: Array.reduce() MDN
Here's a little example of how you might use it in this context
function sumAllApplesIn(baskets){
return baskets.reduce((accumulator, currentBasket) => accumulator + currentBasket.apples, 0)
}
const baskets = [
{ basket: "one", apples: 10},
{ basket: "two", apples: 15},
{ basket: "three", apples: 30}
];
console.log(sumAllApplesIn(baskets));
You simply need to loop through the people array, grab the 'person' object, and then access the age key. Sum those up and voila
function getSummedAge(people) {
let summedAge = 0;
for (let pdx = 0, plen = people.length; pdx < plen; pdx++) {
let person = people[pdx];
summedAge += person.age;
}
return summedAge
}
const examplePeopleArray = [
{ name: 'John', age: 10 },
{ name: 'Jack', age: 20 },
{ name: 'Jane', age: 25 }
];
console.log(getSummedAge(examplePeopleArray));
I have an object of students & I'd like to get the name of the youngest student.
const students = [
{ name: 'Hans', age: 3 },
{ name: 'Ani', age: 7 },
{ name: 'Budi', age: 10 },
{ name: 'Lee', age: 13 }
]
I get the youngest age by this
function getYoungestAge(data) {
return resultMin = data.reduce((min, p) => p.age < min ? p.age : min, data[0].age)
}
getYoungestAge(students) // return 3
How can I return not only age but also name ? // example: 3 and Hans
You can always take the whole object through your reduce and not just the age
const students = [
{ name: 'Hans', age: 3 },
{ name: 'Ani', age: 7 },
{ name: 'Budi', age: 10 },
{ name: 'Lee', age: 13 }
]
function getYoungestAge(data) {
return data.reduce((min, p) => p.age < min.age ? p : min, data[0])
}
var youngest = getYoungestAge(students)
console.log(youngest);
Another way would be to sort the list and take the first. NOTE: This way changes the original array. That is fine in some cases and undesirable in others. I prefer the first way above in most cases.
const students = [
{ name: 'Hans', age: 3 },
{ name: 'Ani', age: 7 },
{ name: 'Budi', age: 10 },
{ name: 'Lee', age: 13 }
]
function getYoungestAge(data) {
data.sort( (x,y) => x.age-y.age);
return data[0];
}
var youngest = getYoungestAge(students)
console.log(youngest);
Also note that both of these solutions return the first item with the lowest age where more than 1 student shares the same age.
You could return an array with the filtered objects. This works with a single loop and if some people have the same mininmum age, then all peoples with that age are returned.
function getYoungestAge(data) {
return data.reduce(function (r, o, i) {
if (!i || o.age < r[0].age) {
return [o];
}
if (o.age === r[0].age) {
r.push(o)
}
return r;
}, []);
}
const students = [{ name: 'Hans', age: 3 }, { name: 'Ani', age: 7 }, { name: 'Budi', age: 10 }, { name: 'Lee', age: 13 }]
console.log(getYoungestAge(students));
Reduce the array to a Map, with the age as key, and the value an array of all the students with that age. To get the youngest, find the min of the the map's keys, and get the value of that key from the map:
const students = [{"name":"Hans","age":3},{"name":"Ani","age":7},{"name":"Morgan","age":3},{"name":"Budi","age":10},{"name":"Lee","age":13}]
const studentsByAgeMap = students.reduce((m, o) => {
m.has(o.age) || m.set(o.age, [])
m.get(o.age).push(o)
return m
}, new Map())
const result = studentsByAgeMap.get(Math.min(...studentsByAgeMap.keys()))
console.log(result)
Using Lodash the above can be achieved in one line.
_.minBy(students, function(o) {return o.age})
I want to append a new array to an existing array.
var input1 = [ { name: 'one' }, { name: 'two' }, { name: 'three' } ];
var input2 = [ { age: '1' }, { age: '2' }, { age: '3' } ];
result = [ { name: 'one', age: '1' }, { name: 'two', age: '2' }, { name: 'three', name: 'three' } ];
here is my attempt, but it is not working:
var original = "one,two,three";
var myarray = [{ age: '1' }, { age: '2' }, { age: '3' }];
// this myarray could ALSO be an empty [] array.
myarray += original.split(',').map(s => ({name: s}));
console.log(myarray)
Please help to achieve this result. thanks
(this is not a duplicate question, cuz we are dealing with possible difference in the length of one of the arrays).
If you need to handle the case where the two input arrays are different sizes, you'll have to iterate over the maximum length and output to an array a concatenation of the two input objects. Here's an example of how that could be done:
var input1 = [ { name: 'one' }, { name: 'two' }, { name: 'three' } ];
var input2 = [ { age: '1' }, { age: '2' }, { age: '3' } ];
var output = [];
var maxLength = Math.max(input1.length, input2.length);
for (var i = 0; i < maxLength; i++) {
// here we create a new object which is a combination
// of the item from both input arrays and add it to output
output.push(Object.assign({}, input1[i], input2[i]));
}
console.log(output);
The output array would be the length of the longest input array.
If you want to merge the objects at the same indexes in the two arrays and then return a new array you can do something like this:
result = input1.map((obj, index) => ({
...obj,
...input2[index]
}))
edit with working snippet:
const input1 = [ { name: 'one' }, { name: 'two' }, { name: 'three' } ];
const input2 = [ { age: '1' }, { age: '2' }, { age: '3' } ];
function mergeObjectsInArray(arr1, arr2) {
return arr1.map((obj, index) => ({
...obj,
...arr2[index]
}));
}
console.log(mergeObjectsInArray(input1, input2))
If both arrays have the same length, you can loop trough one of them, and you can use Object.assign() to merge objects. For example:
var input1 = [ { name: 'one' }, { name: 'two' }, { name: 'three' } ];
var input2 = [ { age: '1' }, { age: '2' }, { age: '3' } ];
var x = [];
for (i in input1) {
x[i] = Object.assign(input1[i], input2[i])
}
the variable x will hold the value you desire.
I have two arrays that I would like to compare and provide a count of the items in the master list.
The master list might look like this:
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
The Filter list gets all possible names / ages from the database. Some might not have any entries. Each of these lists are getting pulled from an API individually. I will combine them into one array:
{ users:
[{ username: 'Jon' },
{ userName: 'Steve' },
{ username: 'Mark' },
{ username: 'Mike' }],
ages:
[{age: 33},
{age: 34},
{age: 35},
{age: 36}]
}
What I would like to do is be able to count how many of each name I have
Jon - 2, Steve - 1, Mark - 1, Mike - 0
33 - 1, 34 - 2, 35 - 1
Here is a generic approach. You provide the data and the field you want to count.
var data = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
function countUnique(items, property) {
return items.reduce(function(map, item) {
if (item.hasOwnProperty(property)) {
map[item[property]] = (map[item[property]] || 0) + 1;
}
return map;
}, {});
}
console.log(countUnique(data, 'name')); // Object {Jon: 2, Steve: 1, Mark: 1}
console.log(countUnique(data, 'age')); // Object {33: 1, 34: 2, 35: 1}
Filtering
If you want to filter a list of users by conditions, you can define an array of filter objects as seen below. When filtering a list of items, you usually will provide a predicate function to execute on the current item in the filter call. This function returns a boolean value which determines whether or not the item meets the conditions of the function.
var users = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
var filters = [{
name: 'users',
predicate : function(user) {
return [ 'Jon', 'Mark', 'Mike' ].indexOf(user.name) > -1;
}
}, {
name: 'ages',
predicate : function(user) {
return user.age >= 34 && user.age <= 36;
}
}];
print(filterFactory(users, getFiltersByName(filters, ['users', 'ages'])));
function getFiltersByName(filters, names) {
return filters.filter(function(filter) {
return names.indexOf(filter.name) > -1;
});
}
function filterFactory(items, filters) {
return items.filter(function(item) {
return filters.some(function(filter) {
try { return filter.predicate.call(undefined, item); }
catch (e) { throw new Error('predicate undefined for filter: ' + filter.name); }
});
});
}
function print(obj) {
document.body.innerHTML = JSON.stringify(obj, undefined, ' ');
}
body { font-family: monospace; white-space: pre }
Something like this would do. Here is a fiddle http://jsfiddle.net/5jkqv6k3/
var data = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
var key = function(obj) {
// Some unique object-dependent key
return obj.name; // Just an example
};
var dict = {};
for (var i = 0; i < data.length; i++) {
if (dict[key(data[i])])
dict[key(data[i])] = dict[key(data[i])] + 1;
else
dict[key(data[i])] = 1;
}
console.log(dict);
Using angularJs (because you're using it as you said) you can do this:
var countNamesList = {};
var countAgesList = {};
angular.forEach(masterList, function(value, index) {
countNamesList[masterList[index].name] =
(!angular.isUndefined(countNamesList[masterList[index].name])) ?
countNamesList[masterList[index].name] + 1 : 1;
countAgesList[masterList[index].age] =
(!angular.isUndefined(countAgesList[masterList[index].age])) ?
countAgesList[masterList[index].age] + 1 : 1;
});
console.log(countNamesList);
console.log(countAgesList);
JSFIDDLE
Mr. Polywhirl's answer is your best option on counting.
Now here's how you can filter:
var master = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
var filter = {
users: [
{ username: 'Jon' },
{ username: 'Mark' },
{ username: 'Mike' }
], ages: [
{ age: 34 },
{ age: 35 },
{ age: 36 }
]
};
function filterByNameAndAge(obj) {
return filter.users.some(function(user) {
return user.username === obj.name;
}) && filter.ages.some(function(age) {
return age.age === obj.age;
});
}
console.log(master.filter(filterByNameAndAge));
Currently it accepts only objects with matching name and age. Replace the && inside filterByNameAndAge by || if it should accept objects with matching name or age.