How to get the average from array of objects - javascript

Below I reproduce my code for calculate average ratings for foods.
3+4+5+2 / 4 should be equal to 3.5.
But this not gives me the correct output. What's wrong with my code?
const ratings = [
{food:3},
{food:4},
{food:5},
{food:2}
];
let food = 0;
ratings.forEach((obj,index)=>{
food = (food + obj.food)/++index
})
console.log("FOOD",food)

Despite of having several answers I would like to add mine focusing to improve the code quality:
You can use destructuring in forEach() to just get the food property of the object as that is only the property you are interested with.
Despite of dividing inside loop we can have only one division operation after the loop is completed. This saves a lot of computation when the array is huge(thousands of objects)
You can use short hand like += in the loop for summing up the value.
You can make a single line code in forEach()
const ratings = [
{food:3},
{food:4},
{food:5},
{food:2}
];
let foodTotal = 0;
let length = ratings.length;
ratings.forEach(({food})=> foodTotal += food);
console.log("FOOD",foodTotal/length);

Here is another solution using .reduce()
const ratings = [
{food: 3},
{food: 4},
{food: 5},
{food: 2}
]
const average = ratings.reduce((a, b) => {
return {food: a.food + b.food}
}).food / ratings.length
console.log('FOOD', average)

You'd use reduce to sum, then simply divide by rating's length
const ratings = [
{food:3},
{food:4},
{food:5},
{food:2}
];
const avg = ratings.reduce((a, {food}) => a + food, 0) / ratings.length;
console.log(avg);

You need to divide by the number of elements after you have summed them together
const ratings = [
{food:3},
{food:4},
{food:5},
{food:2}
];
let food = 0;
ratings.forEach((obj,index)=>{
food = (food + obj.food)
});
food = food / ratings.length;
console.log("FOOD",food)

You need to add the relative contribution to the average of each food.
Since average is - sum of items / number of items in your case it's
(3+4+5+2) / 4
Which we can split to
3/4 + 4/4 + 5/4 + 2/4
const ratings = [{"food":3},{"food":4},{"food":5},{"food":2}];
let food = 0;
ratings.forEach((obj) => {
food = food + obj.food / ratings.length;
})
console.log("FOOD", food)
You can also use Array.reduce() to shorten the code a bit:
const ratings = [{"food":3},{"food":4},{"food":5},{"food":2}];
const food = ratings.reduce((r, { food }) =>
r + food
, 0) / ratings.length;
console.log("FOOD", food)

What you are doing is 3/1 + 4/2 + 5/3 + 2/4 which is not equivalent to 3/4 + 4/4 + 5/4 + 2/4. What you can do is
ratings.forEach((obj,index)=>{
food = (food + obj.food)
});
food = food / ratings.length;
console.log("FOOD",food)
If you are into functional way of doing things then I would use a reduce function to get the result.
const food = ratings.reduce((sum, { food }) => sum + food, 0) / ratings.length;
console.log("FOOD", food)

Related

Randomly swap 2 elements of array with another 2 elements

I have a list of team members and a list of 2 substitutes:
team = Samson, Max, Rowan, Flynn, Jack
subs = Struan, Harry
I need to randomly swap the 2 subs into the team. I'm getting stuck because I need to ensure that only these 2 elements are swapped and that both are swapped in. I tried just looping through the subs array and randomly swapping each element with an element from the team array, but too frequently it swapped Struan with Max and then Harry with Struan, so that in the end Struan was still in the subs array and not in the team.
So: I need to exclusively swap the 2 elements in the sub array with random elements from the team array. How can I do that?
You can do this with this function. Give the function current team and substitudes and get new team with random substitution done.
const team = ['Samson', 'Max', 'Rowan', 'Flynn', 'Jack']
const subs = ['Struan', 'Harry']
const substitudePlayers = (team, subs) => {
const newTeam = [...team]
// get 2 random numbers which are less than team members count
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max-1);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 2 random numbers
let num1 = getRandomInt(0, team.length)
let num2 = getRandomInt(0, team.length)
// make sure that numbers are different
while(num2 === num1) num2++
// substitude
newTeam[num1]=subs[0]
newTeam[num2]=subs[1]
return newTeam
}
const substitudedTeam = substitudePlayers(team, subs);
console.log(substitudedTeam)
This should work:
function getRandomItem () {
let randomItem = team[Math.floor(Math.random()*team.length)];
if(subs.indexOf(randomItem)<0){
return randomItem;
} else {
return getRandomItem();
}
}
subs.forEach((item,i)=>{
subs.splice(i,1,getRandomItem());
}

Get pseudo-random item with given probability

I want to give the user a prize when he signs in;
but it needs to be there some rare prizes so I want to appear prizes with different chances to appear using percents
i want to display one of these
[50 : 'flower'], [30 : 'book'], [20 : 'mobile'];
using percents they have
if there any way using Node.js or just javascript functions it would be great
You can create a function to get weighted random results, something like this:
const weightedSample = (items) => {
// cache if necessary; in Chrome, seems to make little difference
const total = Object.values(items).reduce((sum, weight) => sum + weight, 0)
const rnd = Math.random() * total
let accumulator = 0
for (const [item, weight] of Object.entries(items)) {
accumulator += weight
if (rnd < accumulator) {
return item
}
}
}
// check frequencies of each result
const prizes = { flower: 50, book: 30, mobile: 20 }
const results = Object.fromEntries(Object.keys(prizes).map(k => [k, 0]))
for (let i = 0; i < 1e6; ++i) {
const prize = weightedSample(prizes)
++results[prize]
}
// sample results: { flower: 500287, book: 299478, mobile: 200235 }
console.log(results)
This will work regardless of whether the weights add up to 100, whether they're integers, and so on.
'Right off the top of my head'-approach would be to prepare an array where each source item occurs the number of times that corresponds to respective probability and pick random item out of that array (assuming probability value has no more than 2 decimal places):
// main function
const getPseudoRandom = items => {
const {min, random} = Math,
commonMultiplier = 100,
itemBox = []
for(item in items){
for(let i = 0; i < items[item]*commonMultiplier; i++){
const randomPosition = 0|random()*itemBox.length
itemBox.splice(randomPosition, 0, item)
}
}
return itemBox[0|random()*itemBox.length]
}
// test of random outcomes distribution
const outcomes = Array(1000)
.fill()
.map(_ => getPseudoRandom({'flower': 0.5, 'book': 0.3, 'mobile': 0.2})),
distribution = outcomes.reduce((acc, item, _, s) =>
(acc[item] = (acc[item]||0)+100/s.length, acc), {})
console.log(distribution)
.as-console-wrapper{min-height:100%;}
While above approach may seem easy to comprehend and deploy, you may consider another one - build up the sort of probability ranges of respective width and have your random value falling into one of those - the wider the range, the greater probability:
const items = {'flower': 0.5, 'book': 0.2, 'mobile': 0.2, '1mUSD': 0.1},
// main function
getPseudoRandom = items => {
let totalWeight = 0,
ranges = [],
rnd = Math.random()
for(const itemName in items){
ranges.push({
itemName,
max: totalWeight += items[itemName]
})
}
return ranges
.find(({max}) => max > rnd*totalWeight)
.itemName
},
// test of random outcomes distribution
outcomes = Array(1000)
.fill()
.map(_ => getPseudoRandom(items)),
distribution = outcomes.reduce((acc, item, _, s) =>
(acc[item] = (acc[item]||0)+100/s.length, acc), {})
console.log(distribution)
"Certain probability" and "random" could lead to different approaches!
If you want random each time, something like:
let chances = [[0.2,'mobile'],[0.5,'book'],[1.0,'flower']]
let val = Math.random() // floating number from 0 to 1.0
let result = chances.find( c => c[0] <= val )[1]
This will give a random result each time. It could be possible to get 'mobile' 100 times in a row! Rare, of course, but a good random number generate will let that happen.
But perhaps you want to ensure that, in 100 results, you only hand out 20 mobiles, 30 books, and 50 flowers. Then you might want a "random array" for each user. Pre-fill the all the slots and remove them as they are used. Something like:
// when setting up a new user
let userArray = []
let chances = [[20,'mobile'],[30,'book'],[50,'flower']]
changes.forEach( c => {
for(let i = 0; i < c[0]; i++) userArray.push(c[1])
})
// save userArray, which has exactly 100 values
// then, when picking a random value for a user, find an index in the current length
let index = Math.floor(Math.random() * userArray.length)
let result = userArray[index]
userArray.splice(index,1) // modify and save userArray for next login
if(userArray.length === 0) reinitializeUserArray()
There are different approaches to this, but just some ideas to get you started.

Javascript - Get percentage of similarity between array of multiple strings using levenshtein algorithm

I need to get a percentage or score to represent how likely it is that an array of strings are describing the same object.
My attempt at an idea is below, i'm sure there is a better way which is why i'm reaching out here to you smart people.
var levenshtein = require('fast-levenshtein');
var items = [
'Michael Jordan - NBA - Chicago Bulls',
'Michael J - Chicago',
'Michael Jordan - NBA',
'Michael Jordan - NBA - Chicago',
];
function getPercentageOfCertainty(items) {
var firstItem = items[0]
var totalItems = items.length;
var percentagePoints = items.reduce(function(memo, item) {
var distance = levenshtein.get(item, firstItem, { useCollator: true})
memo += distance;
return memo;
}, 0)
return (totalItems * 100) - percentagePoints;
}
var percentage = getPercentageOfCertainty(items);
//0.8, 80% or some other way to score the similarity
My thoughts are to calculate the similarity of each string to the first string in the set, then either get an average or add all of the scores together and divide by the total amount of items.

Javascript adding multiple arrays in a loop

I am trying to add multiple arrays in javascript.
Here are my arrays I have made, and are working.
function getAmountSpent(){
var amountSpent = ((Math.random() * 500) + 1);
return amountSpent.toFixed(2)
}
function getGift(){
var gift = ((Math.random()* 50) + 1);
return gift.toFixed(2)
}
var names = ["Jeremy","Arun","Alisa","Rohan","Dana"];
var spent = [];
for (let i = 0; i < 5; i++) {
spent.push(getAmountSpent());
}
var gifts = [];
for (let i = 0; i<5; i++) {
gifts.push(getGift());
}
What I need help with is adding these arrays in a new function. I have began writing the code, and I am not sure what is wrong.
var totals =[];
for (let i=0; i<5; i++) {
totals.push(getSumTotals())
}
function getSumTotals(a){
totals= spent+(spent * gifts);
return totals.toFixed(2)
}
From what you can see, I am trying to add up the totals much like this:
totals[0] = spent[0] + (spent[0] * gifts[0]);
totals[1] = spent[1] + (spent[1] * gifts[1]);
totals[2] = spent[2] + (spent[2] * gifts[2]);
totals[3] = spent[3] + (spent[3] * gifts[3]);
totals[4] = spent[4] + (spent[4] * gifts[4]);
if it helps, the professor added guided instructions for function getSumTotals(a) stating:
This function will return the sum of the elements in array a.
You will be passing the array that holds your totals to
the parameter a. Be sure to treat the values in a as numbers.
I am not sure if this helps but here is the output to my document.
Current Total should equal (spent) + (spent * gifts). For instance, for Jeremy in this example, current total should equal:
$36.55 + ($36.55*0.0626) = $38.83. Since there are many variables involved, I am not 100% sure what I should write for function getSumTotals(a)
The parameter "a" is a placeholder because I am not sure how many parameter values I need, and the proper format I need to use.
As for the code...
You're both
not passing an index to getSumTotals
not using this parameter within getSumTotals to access your spent and gifts arrays
var totals =[];
for (let i=0; i<5; i++) {
totals.push(getSumTotals(i)) // you were missing i
}
function getSumTotals(idx) { // I took liberties to rename this
totals = spent[idx] + (spent[idx] * gifts[idx]);
return totals.toFixed(2);
}
Now for the Math...
All that said, this math of spent[i] + spent[i] * gifts[i] doesn't make much sense either. Was this specified in the problem?
you may use like this
defined gifts
gifts=[45,43,32];
defined spends
spends=[43,32,21];
this is the getSumTotal funtion
getSumTotal=(x)=>(x.a+x.b)
this is where added
totals=gifts.map((d1,i)=>{
return fu({a:gifts[i],b:spends[i]})
})
I understand this is your assignment, however - if the idea is to both generate arrays, and then add them together, it is a redundant step. Just use the name array to iterate once and do all your calculations within that single loop.
Here, I had some fun and took some liberties, but hopefully you see why multiple arrays are redundant.
function getSumTotals() {
const getAmountSpent = () => Math.random() * 500 + 1;
const getGift = () => Math.random() * 50 + 1;
const names = ["Jeremy", "Arun", "Alisa", "Rohan", "Dana"];
let totals = []
names.forEach((name, i) => {
let spent = getAmountSpent()
let gifts = getGift()
let $$$ = (spent + spent * gifts).toFixed(2);
totals[i] = $$$
console.log(`${name} cost me $${$$$}${'!'.repeat(($$$/1000) | 1)}`)
});
return totals;
}
getSumTotals()
Note, that toString returns a type of "String", but not "Number".
When you try to sum a number with string, you get a concatenated string "1" + 2 = "12"
To turn a string into Number, you must use a Number("str") function, or just a bunary + before the string:
console.log( "1" + 2 );
console.log( Number("1") + 2 );
console.log( +"1" + 2 );
Also, you use the same loop 3 times, but can use just one loop instead, and call all functions inside the one loop. And use your array.length instead of fixed number 5:
let names = ["Jeremy", "Arun", "Alisa", "Rohan", "Dana"];
let spent = [];
let gifts = [];
let totals = [];
for (let i = 0; i < names.length; i++) {
spent.push( getAmountSpent() );
gifts.push( getGift() );
totals.push( getSumTotals(i) );
}
console.log( totals );
function getAmountSpent() {
return rand(1, 500, 2);
}
function getGift() {
return rand(1, 50, 2);
}
function getSumTotals(i) {
return +( spent[i] * ( 1 + gifts[i] ) ).toFixed(2);
}
function rand(from, to, fixed = 0){
return +(Math.random()*( to - from ) + from).toFixed(fixed);
}
P.s. Math.random() returns a number between 0 (included) and 1 (not included). If you need a random number between (example) 20 and 100, Math.random()*(100-20) will give a number between 0 and 80. After adding +20 to the result, you get a number from 20 to 100. That's what does this formula Math.random()*( to - from ) + from
P.P.s. Another way, to get the same thing:
var names = ["Jeremy", "Arun", "Alisa", "Rohan", "Dana"].reduce( (prev, elem) => {
let spent = rand(1, 500, 2);
let gift = rand(1, 50, 2);
prev[elem] = new UserData( spent, gift );
return prev;
}, {});
console.log( "Jeremy spent: " + names.Jeremy.spent );
console.log( names );
function rand(from, to, fixed = 0){
return +(Math.random()*( to - from ) + from).toFixed(fixed);
}
function UserData(spent, gift){
this.spent = spent;
this.gift = gift;
this.total = +(spent * ( 1 + gift )).toFixed(2);
}
/* Google → Array reduce, Constructor functions */
function getAmountSpent(){
let amountSpent = ((Math.random() * 500) + 1);
return Number(amountSpent.toFixed(2))
}
function getGift(){
let gift = ((Math.random()* 50) + 1);
return Number(gift.toFixed(2))
}
let names = ["Jeremy","Arun","Alisa","Rohan","Dana"];
let spent = [];
let gifts = [];
let totals =[];
for (let i = 0; i < names.length; i++) {
spent.push(getAmountSpent());
gifts.push(getGift());
totals[i] = (spent[i]+(spent[i] * gifts[i])).toFixed(2);
totals[i] = parseFloat(totals[i])
}
Hi there
I don't think you need a function to add the totals. you just need to loop through and assign totals[i] to spent[i] + (spent[i] * gifts[i]).
then you can use the parseFloat and toFixed function to change the string to a number. remember toFixed() function turns numbers to string. so you need to use the parseFloat to change it to number again as shown in the code above. or you can come up with an easy way of changing it to number. I hope this helps!

String and Int Array Sorting

I've got a problem sorting arrays. I'm currently trying to optimize a thing in a strategy game I play, and for that I need to calculate distance between all members of my alliance, the first towards the others and so on. No problem doing that actually. But now, what I want to do is sort the array of distance "ascending" and problem is, I need to write the corresponding nickname to match the distance. I've been searching for 2 days and I can't figure out a working solution.
I tried to copy the array before sorting it, but I need the unsorted array and with that sort function, it sorts the copy too !
Actually the code provided is good, speaking of distance accuracy but not sorted ascending. If I sort the distances, the nicknames are no longer corresponding. I don't know why they appear in the order of the pseudo_list because It's supposed to be sorted through nSort2()
This is what I've ended up with so far :
//Sorting Distance[i] Array List
function nSort(arr)
{
return arr.sort((a, b) => a - b);
}
//Calculating Distance
function calcDist(xA, yA, xB, yB)
{
return Math.sqrt(Math.pow((xB-xA), 2)+Math.pow((yB-yA), 2));
}
//Here i'm trying to retrieved unsorted position of distance by index to sort the nicknames by their respective distances
function nSort2(arr_str, arr_nbr)
{
var arr_nbr2 = arr_nbr.splice(0);
var arr_sort = nSort(arr_nbr2);
var str_sort = [];
arr_str.forEach(function(element, i)
{
j = arr_sort.indexOf(arr_nbr2[i], i);
str_sort[i] = arr_str[j];
});
console.log(str_sort);
return str_sort;
}
var pseudo_list = ["teddy95", "gabrielc", "ngozi"]; //The list (I just put the first 3 to not to write to much unnecessary code)
var x_ = [29, 26, 4]; // The X Coordinate list
var y_ = [519, 461, 143]; // The Y Coordinate list
var distance = [[]]; // The 2D Array for distance (distance[0][0] being the member's distance tower himself (which is obviously 0).
//Calculating Distances And Storing them in the 2D Array
y_.forEach(function(element, i)
{
distance[i] = [];
x_.forEach(function(element, j)
{
distance[i][j] = Math.ceil(calcDist(x_[i], y_[i], x_[j], y_[j]));
});
});
//Displaying Sorted Array ascending (Trying)
y_.forEach(function(element, i)
{
x_.forEach(function(element, j)
{
document.write(pseudo_list[i] + ' -> ' + nSort2(pseudo_list, distance[i])[j] + ': ' + distance[i][j] + '<br>');
});
});
I think your problem come from over complicating the data structures (I'm not insulting you just sharing an opinion).
In the code below all the input (pseudo, x, y) is stored in an object so player data is easier to manipulate.
Then I'm not using a matrix because you end up creating new issues namely I'd expect distance[1][2] = distance[2][1] so sorting will create duplicate results (and the diagonal doesn't help since it represents the distance from yourself). Instead I have a 1D array constructed with no duplicates, i.e. it contains the distance from the first element to all the others (i.e. second, third, ...), then the second element from the "ones on the right" (i.e. third, fourth, ...), ...
Once you have all the distance information, sorting is a trivial task so is displaying the result.
//Calculating Distance
function calcDist(xA, yA, xB, yB) {
return Math.sqrt(Math.pow((xB - xA), 2) + Math.pow((yB - yA), 2));
}
let players = [{
pseudo: "teddy95",
x: 29,
y: 519
},
{
pseudo: "gabrielc",
x: 26,
y: 461
},
{
pseudo: "ngozi",
x: 4,
y: 143
}]
let distances = []
players.forEach(function (element, i) {
for (let j = i + 1; j < players.length; ++j) {
distances.push({
player1: element,
player2: players[j],
distance: Math.ceil(calcDist(element.x, element.y, players[j].x, players[j].y))
})
}
})
distances.sort(function (a, b) { return a.distance - b.distance })
distances.forEach(function (element, i) {
document.write(element.player1.pseudo + ' - ' + element.player2.pseudo + ' dist ' + element.distance + '<br>')
})

Categories

Resources