I'm getting data from an api and want to format the data in a way that the client side can read.
I've tried the following code to build up the object, but it feels hacky and doesn't take care of edge cases.
function myHackyFunction(data){
result = {}
data.forEach(el => {
const timeStamp = el['time']
result[timeStamp] = { teacher: null, student: null }
})
data.forEach(el => {
const role = el['role']
const timeStamp = el['time']
const age = el['age']
if (role.includes('teacher')) {
result[timeStamp].teacher = age
}
if (role.includes('student')) {
result[timeStamp].student = age
}
})
return result
}
myHackyFunction(data)
The data variable will have different length, but always the same setup. Sometimes it includes both the student and teacher role, sometimes just one of them.
this data..
const data = [
{
time: 2019,
role: 'student',
age: 22
},
{
time: 2019,
role: 'teacher',
age: 37
},
{
time: 2020,
role: 'teacher',
age: 45
}
]
..should look like:
const desiredData = {
2019: {
student: 22,
teacher: 37
},
2020: {
student: null,
teacher: 45
}
}
Whenever you see data that looks like your grouping etc, Array.reduce would normally fit the bill.
eg.
const data = [
{
time: 2019,
role: 'student',
age: 22
},
{
time: 2019,
role: 'teacher',
age: 37
},
{
time: 2020,
role: 'teacher',
age: 45
}
];
//..should look like:
const desiredData = data.reduce((a, v) => {
a[v.time] = a[v.time] || {student: null, teacher: null};
a[v.time][v.role] = v.age;
return a;
}, {});
console.log(desiredData);
Find the solution below for the problem:
console.clear()
const data = [
{
time: 2019,
role: 'student',
age: 22
},
{
time: 2019,
role: 'teacher',
age: 37
},
{
time: 2020,
role: 'teacher',
age: 45
}
]
const m = {}
const len = data.length
for (let i = 0; i < len; ++i) {
if (!m[data[i].time]) m[data[i].time] = {student: null, teacher: null}
if (data[i].role === 'student')
m[data[i].time].student = data[i].age
else if (data[i].role === 'teacher')
m[data[i].time].teacher = data[i].age
}
console.log(m)
Note: This will only work if there is one student/teacher in a given year, otherwise the value will be overridden.
Related
I need to find if values inside two different arrays of objects is equal. This is an example of what i need:
https://jsfiddle.net/5cb1xsq2/10/
I need to compare the object1 and object2 arrays, and show only the object1 array with the same 'years' value of the object2 array.
This is the result for this case:
{
'name': 'john',
'surname': 'doe',
'years': 29
}
Thank you!
var array1 = [
{
name: "john",
surname: "doe",
years: 29,
},
{
name: "tiler",
surname: "phillis",
years: 50,
},
{
name: "mathias",
surname: "terry",
years: 45,
},
];
var array2 = [
{
name: "mary",
surname: "poppins",
years: 32,
},
{
name: "mickey",
surname: "mouse",
years: 29,
},
{
name: "minnye",
surname: "mouse",
years: 36,
},
];
var results = array1.filter(parentObj => array2.filter(childObj => childObj.years == parentObj.years).length > 0);
If there is only one match, then something like this?
let result;
for (let i = 0; i < array2.length; i += 1) {
const a2 = array2[i]
const index = array1.findIndex((a1) => a1.years === a2.years);
if (index > -1) {
result = array1[index];
break;
}
}
You can filter the first array using the result of filtering the second array with each element comparison.
var array1 = [
{ years: 29 },
{ years: 50 },
{ years: 60 }
];
var array2 = [
{ years: 29 },
{ years: 30 },
{ years: 50 }
];
console.log(array1.filter(x => array2.filter(y => y.years == x.years).length > 0));
Hello I'm having a hard time getting this complex return using MongoDB nor Javascript. Hope can anyone teach me how to get this return.
Admin.
group result by id
user
flatten result
Here's the data example.
let user = [
{
_id: 123,
name: 'John',
createdAt: "2015-08-12T00:00:00Z"
},
{
_id: 124,
name: 'Jane',
createdAt: "2015-09-12T00:00:00Z"
},
{
_id: 125,
name: 'Robert',
createdAt: "2015-09-12T00:00:00Z"
},
{
_id: 126,
name: 'Samson',
createdAt: "2016-11-12T00:00:00Z"
}
]
Expected Result
user
[
{
"15-8": 1 //yyyy-mm: number of data for the month of august
},
{
"15-9": 2
},
{
"16-11": 1
}
]
admin
[
{
"15-8":
{
_id: 123,
count: 1
}
},
{
"15-9": {
_id: 124,
count: 1,
},{
_id: 125,
count: 1
},
{
"16-11": {
_id: 126,
count: 1
}
}
]
You should have the function to get key from date string named getKeyFromDate with format result YY-MM
Loop user data to aggregate your data by using reduce, for example.
let user = [
{
_id: 123,
name: 'John',
createdAt: "2015-08-12T00:00:00Z"
},
{
_id: 124,
name: 'Jane',
createdAt: "2015-09-12T00:00:00Z"
},
{
_id: 125,
name: 'Robert',
createdAt: "2015-09-12T00:00:00Z"
},
{
_id: 126,
name: 'Samson',
createdAt: "2016-11-12T00:00:00Z"
}
];
const getKeyFromDate = (dateString) => {
var date = new Date(dateString);
var year = date.getFullYear().toString().substr(2, 3);
var month = date.getMonth() + 1;
return `${year}-${month}`; // YY-MM
};
var result = user.reduce((acc, {createdAt}) => {
var key = getKeyFromDate(createdAt);
acc[key] = acc[key] || {[key]: 0}; //Also use shortcut like: acc[key] ??= {[key]: 0};
acc[key][key] += 1;
return acc;
}, {});
console.log(Object.values(result));
You can also use for..of if you're not familiar with .reduce
var result2 = {};
for(let {createdAt} of user){
var key = getKeyFromDate(createdAt);
result2[key] = result2[key] || {[key]: 0};
result2[key][key] += 1;
}
console.log(Object.values(result2));
I want to make an array of objects grouped by the date property.
let data = [
{ Age: "(60-100)", Date: "28/05/20" },
{ Age: "(60-100)", Date: "28/05/20" },
{ Age: "(4-6)", Date: "28/05/20" },
{ Age: "(60-100)", Date: "29/05/20" },
{ Age: "(38-43)", Date: "29/05/20" },
{ Age: "(4-6)", Date: "29/05/20" },
{ Age: "(38-43)", Date: "30/05/20" },
{ Age: "(38-43)", Date: "30/05/20" }
];
I want the output like
result = [
{ Date: "28/05/20", "(60-100)": 2, "(4-6)": 1 },
{ Date: "29/05/20", "(38-43)": 1, "(4-6)": 1, "(60-100)": 1 },
{ Date: "30/05/20", "(38-43)": 2 },
]
Try this:
function groupByDate(data){
let groupedData = [];
data.forEach(element => {
//Search for the object containing the specified date
let objIndex = groupedData.findIndex(object => {return object.Date == element.Date;})
//If date is not created, create it
if (objIndex == -1){
groupedData.unshift({Date: element.Date})
objIndex = 0;
}
//If age is not created, create it. Else add 1 to specified age.
if(typeof groupedData[objIndex][element.Age] == 'undefined'){
groupedData[objIndex][element.Age] = 1;
} else {
groupedData[objIndex][element.Age]++;
}
});
return groupedData;
}
If you also want to sort by date, you could check out this post.
Hope it helped you!
Give this a try.
let data = [
{ Age: "(60-100)", Date: "28/05/20" },
{ Age: "(60-100)", Date: "28/05/20" },
{ Age: "(4-6)", Date: "28/05/20" },
{ Age: "(60-100)", Date: "29/05/20" },
{ Age: "(38-43)", Date: "29/05/20" },
{ Age: "(4-6)", Date: "29/05/20" },
{ Age: "(38-43)", Date: "30/05/20" },
{ Age: "(38-43)", Date: "30/05/20" }
];
let res = [];
data.map((d, index) => {
if (!res.some(val => val.Date === d.Date)) {
d[`${d.Age}`] = 1
res.push(d)
delete(d.Age)
} else {
let index = res.findIndex(val => val.Date == d.Date);
let _d = res[index];
if (_d.hasOwnProperty(`${d.Age}`)) {
_d[`${d.Age}`] = parseInt(_d[`${d.Age}`] + 1)
} else {
_d[`${d.Age}`] = 1
}
res[index] = _d;
}
})
console.log(res)
try this:
var result={};
for(var item of data) {
if(result[item.Date]==undefined) result[item.Date]={};
if(result[item.Date][item.Age]==undefined) result[item.Date][item.Age]=0;
result[item.Date][item.Age]++;
}
This gives you an object (not an array) with keys of Date and values as object with keys of Age and values as count.
If you still need an array, you can iterate over the result and construct an array.
result=={
"28/05/20": {
"(60-100)": 2,
"(4-6)": 1
},
"29/05/20": {
"(60-100)": 1,
"(38-43)": 1,
"(4-6)": 1
},
"30/05/20": {
"(38-43)": 2
}
}
If you want the array, you can create resultArr=[], iterate over keys of result, tempObj, add key "Date" and value of iterated key, then iterate over keys of iterated key, add each "Age" with it's count, then push tempObj into resultArr...
Condensed version based on #Dante Culaciati approach with optional sort parameter.
const condenseAge = (arr, isSort = true) => {
let r = [];
arr.map((val) => {
let i = r.findIndex(obj => obj.Date == val.Date);
(i < 0) && r.unshift({Date: val.Date}) && (i = 0);
(!r[i][val.Age]) ? r[i][val.Age] = 1 : r[i][val.Age]++;
});
return !isSort?r:r.sort((a,b)=>(ac=a['Date'].split('/'), bc=b['Date'].split('/'), new Date(ac[2],ac[1],ac[0]) - new Date(bc[2],bc[1],bc[0])));
}
console.log(condenseAge([
{ Age: "(4-6)", Date: "02/06/20"},
{ Age: "(60-100)", Date: "28/05/20" },
{ Age: "(60-100)", Date: "28/05/20" },
{ Age: "(4-6)", Date: "28/05/20" },
{ Age: "(60-100)", Date: "29/05/20" },
{ Age: "(38-43)", Date: "29/05/20" },
{ Age: "(4-6)", Date: "29/05/20" },
{ Age: "(38-43)", Date: "30/05/20" },
{ Age: "(38-43)", Date: "30/05/20" }
]));
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)));
Let's say I have an array with objects like this:
const persons = [
{
name: "Erik",
age: 45
},
{
name: "Bob",
age: 37
},
{
name: "Erik",
age: 28
},
{
name: "Jasper",
age: 29
},
{
name: "Erik",
age: 34
}
];
How do I find the value based on a key with the most occuring value?
In this example, when passing name as key that would be Erik.
Something like this:
const deepMode = (array, key) => {
// Perhaps something with .reduce?
}
And when called, it should return:
deepMode(persons, "name"); // Returns "Erik"
You could take a Map, count the ocurrences and reduce the key/value pairs for getting the max valaue. Return the key without iterating again.
const
deepMode = (array, key) => Array
.from(array.reduce((m, o) => m.set(o[key], (m.get(o[key]) || 0) + 1), new Map))
.reduce((a, b) => a[1] > b[1] ? a : b)[0],
persons = [{ name: "Erik", age: 45 }, { name: "Bob", age: 37 }, { name: "Erik", age: 28 }, { name: "Jasper", age: 29 }, { name: "Erik", age: 34 }];
console.log(deepMode(persons, 'name'));
you can count keys by adding them in object and checking if key exists in object,then increment value, if not then add key into object, after that with Object.keys get keys of object sort them and get first element which is most occurring
const persons = [
{
name: "Erik",
age: 45
},
{
name: "Bob",
age: 37
},
{
name: "Erik",
age: 28
},
{
name: "Jasper",
age: 29
},
{
name: "Erik",
age: 34
}
];
const deepMode = (array, key) => {
const obj = {};
array.forEach(v => {
if (obj[v[key]]) {
obj[v[key]] += 1;
} else {
obj[v[key]] = 1;
}
});
return Object.keys(obj).sort((a,b) => obj[b]-obj[a])[0];
}
console.log(deepMode(persons, 'name'));
You could reduce into a Map, find the max, then find and return it.
function findMostOccuringKeyValue(arr, key) {
const grouped = arr.reduce((a, b) => a.set(b[key], (a.get(b[key]) || 0) + 1), new Map);
const max = Math.max(...grouped.values());
return [...grouped].find(([k, v]) => v === max)[0];
}
console.log(findMostOccuringKeyValue(persons, 'name'));
<script>
const persons = [
{
name: "Erik",
age: 45
},
{
name: "Bob",
age: 37
},
{
name: "Erik",
age: 28
},
{
name: "Jasper",
age: 29
},
{
name: "Erik",
age: 34
}
];
</script>