How to calculate the average value of the properties of an object? - javascript

How can I calculate the average value of the properties of an object?
I have this object:
let components = {
co: [235, 465, 78],
no: [409, 589, 98],
nh3: [54, 76, 39]
};
I need to separately calculate the average for co, no, and nh3.

You could get the entries and build a new object with averages.
const
getAverage = array => array.reduce((a, b) => a + b) / array.length,
components = { co: [235, 465, 78], no: [409, 589, 98], nh3: [54, 76, 39] },
result = Object.fromEntries(Object
.entries(components)
.map(([k, v]) => [k, getAverage(v)])
);
console.log(result);

I have corrected your invalid object and tried to get the average values in an array:
let components = {
co: [235, 465, 78],
no: [409, 589, 98],
nh3: [54, 76, 39]
};
let result = [];
Object.keys(components).forEach(function (key) {
avg = components[key].reduce((a, b) => a + b, 0) / components[key].length;
result.push(avg);
});
console.log(result);

Have a proper object
you can use reduce/length
I use a trick to shorten the decimals, toFixed with a unary plus to convert back to number
There are more tricks here. The object.assign converts the array of object to one object using the ... spread operator
const components = {
"co": [235, 465, 78],
"no": [409, 589, 98],
"nh3": [54, 76, 39]
}
const avgs = Object
.assign({},
...Object.entries(components)
.map(([key,values]) =>
({[key]: +(values
.reduce((a,b)=>a+b)/values.length)
.toFixed(2)})
)
);
console.log(avgs)

Related

Why is my JS recursion function returning a list populated with the same entry?

I've been learning Python at school, and I am learning JavaScript on my own time and tackling some JS projects. I can't figure out why my recursion function is only a list with the same entry.
Function description:
The function takes in a list of course Objects, with key-value pairs "courseCode": string and "possibleCombos": list[number]. I want my recursive function to output another list of Objects, with the course Object's "courseCode" value as its keys, and one element of the "possibleCombos" as its value. The returned list will have all the possible permutations of the Objects with course-combo pairs. The function also takes in an Object parameter, for recursion purposes.
Example data:
const dummyObject1 = {
'courseCode': 'BLUE',
'possibleCombos': [1, 2, 3, 4, 5]
}
const dummyObject2 = {
'courseCode': 'RED',
'possibleCombos': [11, 22, 33, 44]
}
const dummyObject3 = {
'courseCode': 'PURPLE',
'possibleCombos': [111, 222, 333, 444, 555, 666]
}
const dummyList = [dummyObject1, dummyObject2, dummyObject3]```
I ideally want:
let dummySchedules = recursionFunction(dummyList, {})
console.log(dummySchedules)
//ideal console output
[
{'BLUE': 1, 'RED': 11, 'PURPLE': 111},
{'BLUE': 1, 'RED': 11, 'PURPLE': 222},
{'BLUE': 1, 'RED': 11, 'PURPLE': 333},
... //and so on.
]
However, the list output I get, is just 120 entries of the same Object.
Here is my code:
function recursiveFunction(listOfCourses, dictSoFar) {
//base case, checks if listOfCourses is empty
if (!listOfCourses.length) {
return [dictSoFar]
} else {
//recursive step
var arraySoFar = [] //accumulator
//iterate through each element of listOfCourses[0]['possibleCombos']
for (let combo of listOfCourses[0]['possibleCombos']) {
//update dictSoFar entry.
dictSoFar[listOfCourses[0]['courseCode']] = combo
//filter out the course we just entered into dictSoFar.
let course = listOfCourses[0]
var cloneListOfCourses = listOfCourses.filter(item => item !== course)
//recursive call, this time with the filtered out list. If we keep following the
//the recursive call down, it should reach the point where listOfCourses is empty,
//triggering the base case. At that point, dictSoFar already has all course: combo
//pairs. This should traverse through all possible course: combo pairs.
var result = recursiveFunction(cloneListOfCourses, dictSoFar)
//update the accumulator
arraySoFar.push(...result)
}
return arraySoFar;
}
}
What is happening? On theory I think the logic makes sense, and I can't tell where its going wrong.
you can do something like this
if you need some explanation fell free to ask
const dummyObject1 = {
'courseCode': 'BLUE',
'possibleCombos': [1, 2, 3, 4, 5]
}
const dummyObject2 = {
'courseCode': 'RED',
'possibleCombos': [11, 22, 33, 44]
}
const dummyObject3 = {
'courseCode': 'PURPLE',
'possibleCombos': [111, 222, 333, 444, 555, 666]
}
const dummyList = [dummyObject1, dummyObject2, dummyObject3]
function recursiveFunction(listOfCourses) {
const loop = (data, acc) => {
if (!data.length) { // if listOfCourses is falsy
return acc
}
const [next, ...rest] = data
if(acc.length === 0){
return loop(rest, next)
}
return loop(rest, next.flatMap(n => acc.flatMap(a => Object.assign({}, a, n))))
}
const courseCombo = listOfCourses.map(({
courseCode,
possibleCombos
}) => possibleCombos.map(c => ({
[courseCode]: c
})))
return loop(courseCombo, [])
}
console.log(recursiveFunction(dummyList))
I came out with a simpler solution that doesn't involve recursion at all
it's divided in two steps:
the first transformation map you dummy object in an array of elements with this form
[{ BLUE : 1}, { BLUE : 2},{ BLUE : 3}, { BLUE : 4}, { BLUE : 5}]
then using reduce it merges all combination of the three arrays together
const dummyObject1 = {
'courseCode': 'BLUE',
'possibleCombos': [1, 2, 3, 4, 5]
}
const dummyObject2 = {
'courseCode': 'RED',
'possibleCombos': [11, 22, 33, 44]
}
const dummyObject3 = {
'courseCode': 'PURPLE',
'possibleCombos': [111, 222, 333, 444, 555, 666]
}
const dummyList = [dummyObject1, dummyObject2, dummyObject3]
const result = dummyList
.map(({courseCode, possibleCombos}) => possibleCombos.map(c => ({[courseCode]: c})))
.reduce((res, item) => res.flatMap(r => item.flatMap(i => Object.assign({}, r, i))))
console.log(result)
What you're looking for is usually called the Cartesian Product of the lists. With a little fiddling, we can turn your inputs into arrays like [{BLUE: 1}, {BLUE: 2}, /*...,*/ {BLUE: 5}], then do a cartesian product of your collection of these to get something like [[{BLUE: 1}, {RED: 11}, {PURPLE: 111}], [{BLUE: 1}, {RED: 11}, {PURPLE: 222}, /...,*/ [{BLUE: 5}, {RED: 44}, {PURPLE: 666}]]. Then we can just call Object.assign on each of these arrays to get your final result.
The code ends up fairly simple.
const cartesian = ([xs, ...xss]) =>
xs == undefined ? [[]] : xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const spreadCombos = ({courseCode, possibleCombos}) =>
possibleCombos .map (v => ({[courseCode]: v}))
const combine = (os) =>
cartesian (os .map (spreadCombos)) .map (xs => Object .assign ({}, ... xs))
const dummyObject1 = {courseCode: 'BLUE', possibleCombos: [1, 2, 3, 4, 5]}, dummyObject2 = {courseCode: 'RED', possibleCombos: [11, 22, 33, 44]}, dummyObject3 = {courseCode: 'PURPLE', possibleCombos: [111, 222, 333, 444, 555, 666]}
const dummyList = [dummyObject1, dummyObject2, dummyObject3]
console .log (combine (dummyList))
.as-console-wrapper {max-height: 100% !important; top: 0}
cartesian does the cartesian product of an array of arrays.
spreadCombos does that first transformation from your input into [{BLUE: 1}, {BLUE: 2}, /*...,*/ {BLUE: 5}]
And our main function combine first calls spreadCombos on each input element, calls cartesian, and then for each resulting array, calls Object.assign.
Note that we have to start our Object .assign calls with an empty object. In the intermediate format, the instances of, say, {BLUE: 1} are all references to the same object. If we simply spread our array as the only parameters to Object .assign, then we'd be modifying the same reference each time.
This also helps explain what's wrong with your function. You pass through dictSoFar as a reference to an object, and so continually update that same object. You can fix this by passing a clone of the object in your recursive call. For this purpose, we can make do with the shallow clone {...dictSoFar}, although other circumstances might require a deeper clone. So this patch should fix your approach:
- var result = recursiveFunction(cloneListOfCourses, dictSoFar)
+ var result = recursiveFunction(cloneListOfCourses, {...dictSoFar})

.sort not sorting from lowest to highest

while (true) {
let texts = await page.evaluate(() => {
let data = [];
let elements = document.getElementsByClassName("my-2 card__price tw-truncate");
for (var element of elements) {
var floor = Number(element.textContent.split("SOL")[0])
data.push(floor)
}
return data.sort()
});
floorpricearray = texts
if (texts !== undefined) {
console.log(floorpricearray)
}
}
The output
[
10, 11, 5.3, 5.3,
5.38, 5.4, 5.4, 5.4321,
5.4321, 5.5, 5.6942, 5.8,
5.95, 6.2, 7, 7.95,
7.99, 8.05, 8.3, 9
]
You need to give the native .sort method a comparator function like so, otherwise the values will converted to strings and then sorted by the characters ASCII value.
data.sort((a, b) => {
return a - b;
});
You can try this
var a = [10, 11, 5.3, 5.3,
5.38, 5.4, 5.4, 5.4321,
5.4321, 5.5, 5.6942, 5.8,
5.95, 6.2, 7, 7.95,
7.99, 8.05, 8.3, 9
];
a.sort(function(a, b) {
return a - b;
});
alert(a);
It is sorting the array considering the array elements as string. To get it sorted from lowest to highest it needs to be a number.

How to dynamically calculate the array of object values in Javascript?

I have input format as below,
var boxplotInput = [{Day: "01-07-2021", "Thomas": 95, "Diana": 94, "Claura": 93, "Chandler": 93},
{Day: "02-07-2021", "Thomas": 95, "Diana": 94, "Claura": 94, "Chandler": 94},
...
...
{Day: "31-07-2021", "Thomas": 92, "Diana": 94, "Claura": 93, "Chandler": 91}];
I am quite new to javascript objects handling. I have written the code as below to calculate Q1, Q3, and median and it is working fine mathematically the way I am expecting.
//Getting the list of students (excluding date)
var keys;
for(var i = 0; i <boxplotInput.length; i++ ){
keys = Object.keys(boxplotInput[i]).slice(1);
}
////Here, I am hard-coding keys[0]. and getting "Thomas" data only. I am not getting how to avoid for one students only and achieve it for all students.
var studentDataSample = [];
for(var i = 0; i <boxplotInput.length; i++ ){
student1 = boxplotInput[i][keys[0]];
studentDataSample.push(student1);
}
studentDataSample.sort(function(a, b) {return a - b;});
var length = studentDataSample.length;//31
var midIndex = middleIndex(studentDataSample, 0, length);//16
var medianValue = studentDataSample[midIndex];
var Q1 = studentDataSample[middleIndex(studentDataSample, 0, midIndex)];
var Q3 = studentDataSample[middleIndex(studentDataSample, midIndex + 1, length)];
console.log(Q1+", "+medianValue+", "+Q3);// here, the values are fine.
function middleIndex(data, initial, length){
var n = length - initial + 1;
n = parseInt((n + 1) / 2);
return parseInt(n + initial);
}
Something, I understand that it could be achievable through the loop again.. but, not getting how to achieve it for all the students. Kindly, provide the suggestion or idea on this.
Thanks in advance.
if I understand you correctly all need following JS methods:
Array.reduce
Array.filter
Object.keys
The main thing you need here is create useful collection of students with their grades. After this you can calculate all the things you want. In this example I show how to calculate mean.
var boxplotInput = [
{Day: "01-07-2021", "Thomas": 95, "Diana": 94, "Claura": 93, "Chandler": 93},
{Day: "02-07-2021", "Thomas": 95, "Diana": 94, "Claura": 94, "Chandler": 94},
{Day: "31-07-2021", "Thomas": 92, "Diana": 94, "Claura": 93, "Chandler": 91}
];
/*
Get collection of students like:
{
Thomas: [ 95, 95, 92 ],
Diana: [ 94, 94, 94 ],
Claura: [ 93, 94, 93 ],
Chandler: [ 93, 94, 91 ]
}
*/
const students = boxplotInput.reduce((accumulator, currentDay) => {
const students = Object
.keys(currentDay)
.filter(el => el !== 'Day');
students.forEach(student => {
if (!accumulator[student]) {
accumulator[student] = [];
}
accumulator[student].push(currentDay[student]);
});
return accumulator;
}, {});
console.log('Student grades:', students);
// Then we can do anything with it
const studentNames = Object.keys(students);
// Example: finding mean
const studentMeans = studentNames.reduce((acc, student) => {
const grades = students[student];
const sumOfGrades = grades.reduce((acc, cur) => cur + acc, 0);
acc[student] = sumOfGrades / grades.length;
return acc;
}, {});
console.log('Means:', studentMeans);
/*
{
Thomas: 94,
Diana: 94,
Claura: 93.33333333333333,
Chandler: 92.66666666666667
}
*/
I will show you a very clean way to do this using Underscore. Let's inspect all the tools that Underscore and JavaScript provide for this purpose and build our solution one step at a time.
A nice function from Underscore is chain, which lets us massage data in a different shape step by step, while keeping the code very easy to read. For example, you can probably guess what the following chain will do:
var sortedLast = _.chain([2, 3, 1])
.sort()
.last();
console.log(sortedLast);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
chain creates a special wrapper around the input data, which has all Underscore functions as methods. Each method returns a new wrapper, so you can continue to apply more Underscore functions. At the end, you can unwrap the result by calling .value(). In some cases, like in the example above, this happens automatically. last returns the last element of an array.
A nice end shape, which we might want to work towards, could be the following:
{
Thomas: {min: 92, Q1: 93.5, median: 95, Q3: 95, max: 95},
Diana: {min: 94, Q1: 94, median: 94, Q3: 94, max: 94},
Claura: {min: 93, Q1: 93, median: 93, Q3: 93.5, max: 94},
Chandler: {min: 91, Q1: 92, median: 93, Q3: 93.5, max: 94},
}
This is an object which has the same keys as every element of boxplotInput, except for Day. Underscore has an omit function, which lets us do this cleanly, without having to rely on the keys appearing in a particular order:
_.chain(boxplotInput[0])
.omit('Day');
// {Thomas: 95, Diana: 94, Claura: 93, Chandler: 93}
Now we have an object with the correct keys, but wrong values.
mapObject lets us create a new object with the same keys but different values. Besides the input object, it takes a function which will be applied to every key-value pair of the input object in turn. That function takes the value as the first argument and the key as the second argument. Its return value becomes the value at the corresponding key in the new object.
As an intermediate step, let's create an object with a list of all scores per student:
{
Thomas: [95, 95, 92],
Diana: [94, 94, 94],
Claura: [93, 94, 93],
Chandler: [93, 94, 91],
}
In order to achieve this with mapObject, we need to write a function that, given the name of a student, returns an array with the student's scores. Its start will look like this:
function studentScores(firstScore, studentName) {
// code here
}
Let's look at an elegant way to get those scores. In your original code, you wrote something like this (but with key[0] instead of studentName):
var studentDataSample = [];
for (var i = 0; i < boxplotInput.length; i++) {
var student1 = boxplotInput[i][studentName];
studentDataSample.push(student1);
}
Underscore lets you get the same result in a very short line using map:
var studentDataSample = _.map(boxplotInput, studentName);
JavaScript's arrays nowadays have a built-in map method which lets you do something similar. It is not as flexible and concise as Underscore's map, but I'll show how to use it for completeness:
var studentDataSample = boxplotInput.map(dayScores => dayScores[studentName]);
We now know how to write our studentScores:
function studentScores(firstScore, studentName) {
return _.map(boxplotInput, studentName);
}
We don't need the firstScore, but we have to accept it as the first argument anyway, because we are going to pass this function to mapObject, which always passes the value first. Fortunately, we can just ignore it. We can write this function more concisely using the new arrow notation:
(fs, studentName) => _.map(boxplotInput, studentName)
Now we can include this function in our chain, in order to arrive at the intermediate result we previously discussed:
_.chain(boxplotInput[0])
.omit('Day')
.mapObject((fs, studentName) => _.map(boxplotInput, studentName));
// {
// Thomas: [95, 95, 92],
// Diana: [94, 94, 94],
// Claura: [93, 94, 93],
// Chandler: [93, 94, 91]
// }
Let's sort the scores as well, as a preparation for computing the quantiles:
_.chain(boxplotInput[0])
.omit('Day')
.mapObject((fs, studentName) => _.map(boxplotInput, studentName).sort());
// {
// Thomas: [92, 95, 95],
// Diana: [94, 94, 94],
// Claura: [93, 93, 94],
// Chandler: [91, 93, 94]
// }
We can add another mapObject to the chain in order to transform these arrays of sorted scores to the final {min, Q1, median, Q3, max} objects we were aiming for. Since this is not really what your question was about, I will just propose one possible way to do it in functional style:
// A function that returns a function (this is not a typo) that
// computes a particular quantile from a sorted array of numbers.
function quantile(fraction) {
return function(numbers) {
var middle = (numbers.length - 1) * fraction;
return (numbers[Math.floor(middle)] + numbers[Math.ceil(middle)]) / 2;
};
}
// A "blueprint" object with the keys we want to have, each having a
// function to compute the corresponding value from a sorted array of
// scores.
var quantileComputations = {
min: _.first,
Q1: quantile(.25),
median: quantile(.5),
Q3: quantile(.75),
max: _.last,
};
// A function that applies the above blueprint to a given array of
// numbers.
function getQuantiles(numbers) {
return _.mapObject(quantileComputations, f => f(numbers));
}
// Redefining the input data to make this snippet runnable.
var boxplotInput = [
{Day: "01-07-2021", "Thomas": 95, "Diana": 94, "Claura": 93, "Chandler": 93},
{Day: "02-07-2021", "Thomas": 95, "Diana": 94, "Claura": 94, "Chandler": 94},
{Day: "31-07-2021", "Thomas": 92, "Diana": 94, "Claura": 93, "Chandler": 91},
];
// Completing our chain using the above.
var statistics = _.chain(boxplotInput[0])
.omit('Day')
.mapObject((fs, studentName) => _.map(boxplotInput, studentName).sort())
.mapObject(getQuantiles)
.value();
console.log(statistics);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>

How to .map array elements which nested in another array?

I have array todos with elements which is also arrays and contain some numbers.
I need to pass through all arrays, how can I do this with .maps() ?
todos = [
[23, 25],
[33, 36],
[55, 66],
]
console.log(todos.map(el => el.map(subEl => subEl)));
Simply: todos.flat().map(x => x)
you were pretty close but applied the function at the wrong point
I believe this is what you're looking for console.log(todos.map(el => el.map(subEl => subEl)));
todos = [
[23, 25],
[33, 36],
[55, 66],
]
todos.map(el => el.map(subEl => console.log(subEl)));

How to create a variable that is the lowest possible number?

What I have here is an algorithm that finds the highest value in each subarray and pushes it onto a separate array (final).
I want to let the variable 'value' equal to the lowest possible number so that any negative number can be considered higher than 'value'
function largestOfFour(arr) {
var final=[];
arr.map(sub => {
let value = 0; //issue
sub.map(num => {
if(num>value){value=num};
})
final.push(value)
})
return final;
}
console.log(largestOfFour([[17, 23, 25, 12], [25, 7, 34, 48], [4, -10, 18, 21], [-72, -3, -17, -10]]));
In this example the last subarray returns 0 since non of the numbers in that subarray were higher than the initial value of 'value' which is 0.
I want it to return '-3' instead since it's the highest number in the subarray.
It would appear you're simply looking for the max of each array.
Using Array#map and Math#max and spread syntax you could do something like this.
const data = [[17, 23, 25, 12], [25, 7, 34, 48], [4, -10, 18, 21], [-72, -3, -17, -10]];
const res = data.map(arr=>Math.max(...arr));
console.log(res);
You can just set value to Number.NEGATIVE_INFINITY but for what it's worth, I'd recommend simply using reduce instead of map in your inner function. That way, the inner loop will start with sub[0] as an initial value rather than depending on any placeholder.
function largestOfFour(arr) {
var final = arr.map(sub => sub.reduce((num, value) => Math.max(num, value)));
return final;
}
console.log(largestOfFour([
[17, 23, 25, 12],
[25, 7, 34, 48],
[4, -10, 18, 21],
[-72, -3, -17, -10]
]));

Categories