I have an array with names
const players = ['name1','name2','name3','name4','name5','name6','name7','name8'];
const teams = players.length / 2; // -> 4 teams
I want to make teams of 2 people (randomly chosen) and make new arrays from the results -> team
function createTeams() {
for (let i = 0; i < teams; i++) {
for (let x = 0; x < 2; x++) {
// get a random player
selectedPlayer = players[Math.floor(Math.random() * players.length)];
console.log('Selected Player will be added to a team: ', selectedPlayer);
// delete this player from the array
while (players.indexOf(selectedPlayer) !== -1) {
players.splice(players.indexOf(selectedPlayer), 1);
}
// add this player to a new array
//?????
}
}
}
Anyone knows how to do this?
function pickTeams(array, teamSize) {
// Shuffle array, make a copy to not alter original our original array
const shuffled = [...array].sort( () => 0.5 - Math.random());
const teams = [];
// Create teams according to a team size
for (let i = 0; i < shuffled.length; i += teamSize) {
const chunk = shuffled.slice(i, i + teamSize);
teams.push(chunk);
}
return teams;
}
const players = ['name1','name2','name3','name4','name5','name6','name7','name8'];
const teams = pickTeams(players, 2);
You can define a new Array which will contains the teams where you push the two players.
Note that it's better to pass the players array as a parameters of the function and make a swallow copy of it so it won't modify the player array.
const players = ['name1','name2','name3','name4','name5','name6','name7','name8'];
function createTeams(players) {
const playersLeft = [...players]
const newTeams = []
const nbTeams = players.length / 2
for (let i=0; i<nbTeams; i++){
const player1 = playersLeft.splice(Math.floor(Math.random()*playersLeft.length),1)[0]
const player2 = playersLeft.splice(Math.floor(Math.random()*playersLeft.length),1)[0]
newTeams.push([player1, player2])
}
return newTeams
}
console.log(createTeams(players))
Edit : Improve version using a nbPlayerPerTeam parameter
const players = ['name1', 'name2', 'name3', 'name4', 'name5', 'name6', 'name7', 'name8'];
function createTeams(players, nbPlayerPerTeam) {
const playersLeft = [...players]
const newTeams = []
const nbTeams = players.length / nbPlayerPerTeam
for (let i = 0; i < nbTeams; i++) {
const players = []
for (let j = 0; j < nbPlayerPerTeam; j++) {
const player = playersLeft.splice(Math.floor(Math.random() * playersLeft.length), 1)[0]
players.push(player)
}
newTeams.push(players)
}
return newTeams
}
console.log(createTeams(players, 3))
const players = ['name1', 'name2', 'name3', 'name4', 'name5', 'name6', 'name7', 'name8'];
// Divide data by length
const divide = (arr, n) => arr.reduce((r, e, i) =>
(i % n ? r[r.length - 1].push(e) : r.push([e])) && r, []);
const shuffle = (arr) => {
// Deep copy an array using structuredClone
const array = structuredClone(arr);
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle
while (currentIndex != 0) {
// Pick a remaining element
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]
];
}
return array;
}
// Shuffle players
const shuffled = shuffle(players);
// Set a number of people in a team
const groups = divide(shuffled, players.length / 2);
groups.forEach((team, i) => console.log(`team ${i+1}`, JSON.stringify(team)));
Check this updated code, I hope it will work for you. Just create two dimensional array and push players.
const players = ['name1','name2','name3','name4','name5','name6','name7','name8'];
let multi_team=new Array; // new array for team of random 2 players
const teams = players.length / 2; // -> 4 teams
console.log(teams);
createTeams();
function createTeams() {
for (let i = 0; i < teams; i++) {
multi_team[i]= new Array;
for (let x = 0; x < 2; x++) {
// get a random player
selectedPlayer = players[Math.floor(Math.random() * players.length)];
multi_team[i][x]=selectedPlayer;
// delete this player from the array
while (players.indexOf(selectedPlayer) !== -1) {
players.splice(players.indexOf(selectedPlayer), 1);
}
}
}
console.log(multi_team);
}
To differ from previous answers, this is a dynamic way to do it, so you don't care if there are 5 teams or 2, 10 players or 500.
const players = ['j1', 'j2', 'j3', 'j4', 'j5', 'j6', 'j7', 'j8'];
const organizeTeams = (numberOfTeams, players) => {
var teams = [];
// Clone the array to avoid mutations
const freePlayers = [...players];
// How many players will play per team
const playersPerTeam = Math.floor(players.length / numberOfTeams);
// How many player won't have team
const unorganizablePlayers = freePlayers.length % numberOfTeams;
for (let t = 1; t <= numberOfTeams; t++) {
teams = [...teams, pickRandomPlayers(freePlayers, playersPerTeam)];
}
return { teams, playersPerTeam, unorganizablePlayers };
};
const pickRandomPlayers = (players, playersPerTeam) => {
let pickedPlayers = [];
for (let c = 1; c <= playersPerTeam; c++) {
const index = Math.floor(Math.random() * (players.length - 1));
const player = players[index];
pickedPlayers = [...pickedPlayers, player];
// When we pick the player we remove it from the array to avoid duplicates.
players.splice(index, 1);
}
return pickedPlayers;
};
const championship = organizeTeams(3, players);
console.log(`We have ${championship.teams.length} teams.`);
championship.teams.forEach((team, index) => {
console.log(`Team ${index + 1} players: ${team.join(',')}`);
});
console.log(
`There was no place to assign ${championship.unorganizablePlayers} players`
);
The OP needs (to implement ) a reliable shuffle and likewise an array's chunk functionality.
Then one just has to shuffle the provided players array (or a shallow copy of the latter) and to divide the array of randomly ordered player items into chunks, each of custom provided length.
function shuffleArray(arr) {
let idx;
let count = arr.length;
while (--count) {
idx = Math.floor(Math.random() * (count + 1));
[arr[idx], arr[count]] = [arr[count], arr[idx]];
}
return arr;
}
function createListOfChunkLists(arr, chunkLength = 0) {
chunkLength = Math.max(0, chunkLength);
return (chunkLength === 0 && []) ||
arr.reduce((list, item, idx) => {
if (idx % chunkLength === 0) {
list.push([ item ]);
} else {
list[list.length - 1].push(item);
// `at` still not supported by safari.
// list.at(-1).push(item);
}
return list;
}, []);
}
// ... OP ... I have an array with names
const players = ['name1', 'name2', 'name3', 'name4', 'name5', 'name6', 'name7', 'name8'];
// ... OP ... I want to make teams of 2 people (randomly chosen)
// and make new arrays from the results -> team
const teams = createListOfChunkLists(
shuffleArray([...players]), 2
);
console.log({ teams, players });
console.log({
teams: createListOfChunkLists(
shuffleArray([...players]), 2
),
players
});
console.log({
teams: createListOfChunkLists(
shuffleArray([...players]), 4
),
players
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
Related
I want to split an array to sets of 2, having teachers in own chunk if it exist and never have a single item chunk at the end.
const perChunk = 2 // items per chunk
const inputArray = ['teachers', 'art', 'science', 'math', 'language', 'culture']
const result = inputArray.reduce((resultArray, item, index) => {
const chunkIndex = Math.floor(index/perChunk)
if(!resultArray[chunkIndex]) {
resultArray[chunkIndex] = [] // start a new chunk
}
resultArray[chunkIndex].push(item)
return resultArray
}, [])
console.log(result);
for example:
['teachers']
['art', 'science']
['math', 'language', 'culture']
I if understand you want to always add teacher in a single chunk at the begging and the rest divide in equal sizes (2 in this case) and if the last element is not matching the desired state will be added to the element before.
I have this approach
const inputArray = ['teachers', 'art', 'science', 'math', 'language', 'culture']
const arrDivider = (arr, perChunk) => {
const firstElement = "teachers";
const chunks = [];
const length = inputArray.length;
let base = 0;
let final = perChunk
// check if teacher exist in the array
const teacherIndex = arr.indexOf(firstElement);
if(teacherIndex >= 0){
chunks.push([arr[teacherIndex]]) // push it to chunkjs
arr.splice(teacherIndex, 1) // removing teacher from the array
}
for (let i = 0; i < Math.ceil(length / perChunk); i++) {
chunks.push(arr.slice(base, final));
base = final;
final = final + perChunk
}
// If the last item has more than "perChunk" add it to the item before
const lastItem = chunks[chunks.length - 1]
if (lastItem.length != perChunk) {
// If the last element is not the desired size add elements to the element before
chunks[chunks.length - 2].push(...lastItem)
chunks.pop(); // now last item is removed
}
return chunks;
}
const chunks = arrDivider(inputArray, 2)
console.log(chunks)
Check this:
// Define params.
const perChunk = 2;
const firstChunkWords = ['teachers'];
const inputArray = ['teachers', 'art', 'science', 'math', 'language', 'culture'];
// Calc first chunk.
const firstChunk = inputArray.filter(item => firstChunkWords.includes(item));
// Calc other chunks.
const inputArrayWithoutFirstChunkWords = inputArray.filter(item => !firstChunkWords.includes(item));
const otherChunks = inputArrayWithoutFirstChunkWords.reduce((resultArray, item, index) => {
const chunkIndex = Math.floor(index / perChunk);
if (!resultArray[chunkIndex]) {
if (index === inputArrayWithoutFirstChunkWords.length - 1) {
resultArray[chunkIndex - 1].push(item);
return resultArray;
}
resultArray[chunkIndex] = [];
}
resultArray[chunkIndex].push(item);
return resultArray;
}, []);
// Concat chunks.
const result = [firstChunk, ...otherChunks];
// Show result.
console.log(result);
I am trying to create an array of objects that will be populated by random properties.
I want the objects all to look like this
{ systemStar: "something random", planets: ["random1", "random2", etc]}
Currently I have the following code
const letThereBeLight = () => {
let universe = []
const starNumber = 5;
let randomNumberOfPlanets = Math.floor(Math.random() * 6);
for (let i = 0; i < starNumber; i++) {
universe.push({
systemStar: starList[Math.floor(Math.random() * lengthOfStarList)],
systemPlanets: [planetList[Math.floor(Math.random() * lengthOfPlanetList)]]
})
}
console.log(universe)
}
This does a great job in creating the objects I want with the following format, however the systemPlanets is only one, instead of a random number. I have tried to do a double for loop but the syntax was eluding me.
How can I get an array of random strings inside of my for loop?
Added variables for clarity
let starList = [
"Red-Giant",
"Red-Supergiant",
"Blue-Giant",
"White-Dwarf",
"Yellow-Dwarf",
"Red-Dwarf",
"Brown-Dwarf",
];
let planetList = [
"Rocky",
"Temperate",
"Ocean",
"Frozen",
"Lava",
"Gas"
];
Try using Array.from({length:5}) to create an array of 5 elements.
and replace 5 with a random number by using Math.random() * 5 Math.floor() method to round it down to near number, add add one to at least create one planet. then map them to values in source array `planetList.
let starList = [
"Red-Giant",
"Red-Supergiant",
"Blue-Giant",
"White-Dwarf",
"Yellow-Dwarf",
"Red-Dwarf",
"Brown-Dwarf",
];
let planetList = [
"Rocky",
"Temperate",
"Ocean",
"Frozen",
"Lava",
"Gas"
];
let lengthOfStarList = starList.length;
let lengthOfPlanetList = planetList.length;
let universe = []
const starNumber = 5;
for (let i = 0; i < starNumber; i++) {
universe.push({
systemStar: starList[Math.floor(Math.random() * lengthOfStarList)],
systemPlanets: Array.from({length:Math.floor(Math.random() * 5)+1}).map(x=>planetList[Math.floor(Math.random() * lengthOfPlanetList)])
})
}
console.log(universe)
What you need is create another loop of random number that is less than number of available planets, and create an array of planets in that loop:
let starList = [
"Red-Giant",
"Red-Supergiant",
"Blue-Giant",
"White-Dwarf",
"Yellow-Dwarf",
"Red-Dwarf",
"Brown-Dwarf",
];
let planetList = [
"Rocky",
"Temperate",
"Ocean",
"Frozen",
"Lava",
"Gas"
];
const letThereBeLight = () => {
let universe = []
const starNumber = 5;
let randomNumberOfPlanets = Math.floor(Math.random() * 6);
for (let i = 0; i < starNumber; i++) {
const planets = [];
// generate list of planets with at least 1 planet
for(let p = 0, max = ~~(Math.random() * planetList.length - 1) + 1; p < max; p++)
planets[planets.length] = planetList[~~(Math.random() * planetList.length)];
universe.push({
systemStar: starList[Math.floor(Math.random() * starList.length)],
systemPlanets: planets
})
}
console.log(universe)
}
letThereBeLight();
I'm trying to create an algorithm to find duplicate values in a list and return their respective indexes, but the script only returns the correct value, when I have 2 equal elements:
array = [1,2,0,5,0]
result -> (2) [2,4]
Like the example below:
array = [0,0,2,7,0];
result -> (6) [0, 1, 0, 1, 0, 4]
The expected result would be [0,1,4]
Current code:
const numbers = [1,2,0,5,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(numbers.indexOf(avg),numbers.indexOf(avg,n_loop))
};
};
};
return tie;
}
console.log(checkATie(numbers));
if possible I would like to know some way to make this code more concise and simple
Use a Set
return [...new Set(tie)]
const numbers1 = [1,2,0,5,0];
const numbers2 = [0,0,2,7,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(avgList.indexOf(avg),avgList.indexOf(avg,n_loop))
};
};
};
return [...new Set(tie)]
}
console.log(checkATie(numbers1));
console.log(checkATie(numbers2));
I hope this help you.you can use foreach function to check each item of array
var array = [0,0,2,7,0];
var result = [] ;
array.forEach((item , index)=>{
if(array.findIndex((el , i )=> item === el && index !== i ) > -1 ){
result.push(index)
}
})
console.log(result);
//duplicate entries as an object
checkDuplicateEntries = (array) => {
const duplicates = {};
for (let i = 0; i < array.length; i++) {
if (duplicates.hasOwnProperty(array[i])) {
duplicates[array[i]].push(i);
} else if (array.lastIndexOf(array[i]) !== i) {
duplicates[array[i]] = [i];
}
}
console.log(duplicates);
}
checkDuplicateEntries([1,2,0,5,0]);
// hope this will help
Create a lookup object with value and their indexes and then filter all the values which occurred more than once and then merge all indexes and generate a new array.
const array = [1, 2, 0, 5, 0, 1, 0, 2],
result = Object.values(array.reduce((r, v, i) => {
r[v] = r[v] || [];
r[v].push(i);
return r;
}, {}))
.filter((indexes) => indexes.length > 1)
.flatMap(x => x);
console.log(result);
I am trying to find values that commonly appear next to each other in an array.
E.G. given the array:
["dog","cat","goat","dog","cat","elephant","dog","cat","pig","seal","dog","cat","pig","monkey"]
it should return something similar to:
[[["dog","cat"],4],[["cat","pig"],2],[["dog","cat","pig"],2]]
Here is some better data: https://pastebin.com/UG4iswrZ
Help would be greatly appreciated. Here is my current failed attempt at doing something similar:
function findAssociations(words){
var temp = [],tempStore = [],store = [],found = false;
//loop through the words counting occurrances of words together with a window of 5
for(var i = 0;i<words.length-1;i++){
if(i % 5 == 0){
//on every fith element, loop through store attempting to add combinations of words stored in tempStore
for(var j = 0;j<5;j++){
temp = []
//create the current combination
for(var k = 0;k<j;k++){
temp.push(tempStore[k]);
}
//find if element is already stored, if it is, increment the occurrence counter
for(var k = 0;k<store.length;k++){
if(store[k][0]===temp){
found = true;
store[k][1] = store[k][1]+1;
}
}
//if it isn't add it
if(found == false){
store.push([temp,1]);
}
found == false;
}
tempStore = [];
} else {
//add word to tempStore if it i isnt a multiple of 5
tempStore.push(words[i]);
}
}
}
This script is doesn't remove combinations that appear once,it doesn't sort the output by occurrences, nor does it work. It is just an outline of how a possible solution might work (as suggested by benvc).
Here is a generic solution working with multiple group sizes.
You specify a range of group sizes, for example [2,4] for groups of 2 to 4 elements and a minimum number of occurrences.
The function then generates all groups of neighbours of the given sizes, sorts each group and counts the duplicates. The sorting step can be removed is the order in the groups matters.
The duplicates are counted by creating a dictionary whose keys are the group elements sorted and jointed with a special marker. The values in the dictionary are the counts.
It then returns the groups sorted by occurences and then by group size.
const data = ["dog","cat","goat","dog","cat","elephant","dog","cat","pig","seal","dog","cat","pig","monkey"];
function findSimilarNeighbors(groupSizeRange, minOccurences, data) {
const getNeighbors = (size, arr) => arr.reduce((acc, x) => {
acc.push([]);
for (let i = 0; i < size; ++ i) {
const idx = acc.length - i - 1;
(acc[idx] || []).push(x);
}
return acc;
}, []).filter(x => x.length === size);
const groups = [];
for (let groupSize = groupSizeRange[0]; groupSize <= groupSizeRange[1]; ++groupSize) {
groups.push(...getNeighbors(groupSize, data));
}
const groupName = group => group.sort().join('###'); // use a separator that won't occur in the strings
const groupsInfo = groups.reduce((acc, group) => {
const name = groupName(group);
acc[name] = acc[name] || {};
acc[name] = { group, count: (acc[name].count || 0) + 1 };
return acc;
}, {});
return Object.values(groupsInfo)
.filter(group => group.count >= minOccurences)
.sort((a, b) => {
const countDiff = b.count - a.count;
return countDiff ? countDiff : b.group.length - a.group.length;
})
.map(({ group, count }) => [group, count]);
};
console.log(findSimilarNeighbors([2, 4], 2, data));
console.log(findSimilarNeighbors([4, 4], 2, data));
Here is what I came up with. It only finds pairs, but you could modify it to find sets of 3, 4, etc, based on what you % by
const animals = ['dog','cat','goat','dog','cat','elephant','dog','cat','pig','seal','dog','cat','pig','monkey'];
let pairs = ',';
animals.forEach((animal, i) => {
let separator = ',';
if (i % 2 === 0) {
separator = ';'
}
pairs += animal + separator;
});
const evenPairs = pairs.split(',');
const oddPairs = pairs.split(';');
const allPairs = evenPairs.concat(oddPairs).map(pair => pair.replace(/[;,]/, ' '));
let result = {}
allPairs.forEach(pair => {
if (pair.length) {
if (result[pair] === undefined) {
result[pair] = 1;
} else {
result[pair]++;
}
}
});
results in:
dog: 1
cat elephant: 1
cat goat: 1
cat pig: 2
dog cat: 4
elephant dog: 1
goat dog: 1
monkey : 1
pig monkey: 1
pig seal: 1
seal dog: 1
https://stackblitz.com/edit/typescript-wvuvnr
You need to be clear what you mean by close and how close. Just looking at first neighbours you could try:
const findAssociations = words => {
const associations = {}
for (let i = 0; i < words.length - 1; i++) {
const word = words[i]
const wordRight = words[i+1]
const wordOne = word < wordRight ? word : wordRight;
const wordTwo = word < wordRight ? wordRight : word;
const keys = Object.keys(associations)
const key = `${wordOne}:${wordTwo}`
if (keys.indexOf(key) >= 0) {
associations[key]++
} else {
associations[key] = 1
}
}
const keys = Object.keys(associations)
const values = Object.values(associations)
const zipped = keys.map((key, index) => [key, values[index]])
zipped.sort((a, b) => a[1] < b[1] ? 1 : -1);
return zipped;
}
https://stackblitz.com/edit/js-3ppdit
You can use this function inside another function and add every time an element to ["dog", "cat"]
const arr = ["dog", "cat", "goat", "dog", "cat", "dog", "cat", "elephant", "dog", "cat", "pig", "seal", "dog", "cat", "pig", "monkey"]
const findArrayInArray = (arr1, arr2) => {
let count = 0,
arrString1 = arr1.join(""),
arrString2 = arr2.join("");
while (arrString2.indexOf(arrString1) > -1) {
count += 1;
arrString2 = arrString2.replace(arrString1, '');
}
return count;
}
console.log(`["dog", "cat"] exist ${findArrayInArray(["dog", "cat"], arr)} times`)
Assuming each item in the list is a delimiter of a set, and each set counts once for each item (i.e. ["dog", "cat", "goat"] counts as ["dog", "cat"] and ["dog", "cat", "goat"], and assuming you don't want any single occurrences, then here's one way:
const full_list = ["dog","cat","goat","dog","cat","dog","cat","elephant","dog","cat","pig","seal","dog","cat","pig","monkey"];
// create list of unique items
const distinct = (value, index, self) => {
return self.indexOf(value) ===index;
}
const unique_items = full_list.filter(distinct);
// get all patterns
var pre_report = {};
for (var i in unique_items) {
item = unique_items[i];
var pattern = [item];
var appending = false;
for (var j = full_list.indexOf(item) + 1; j < full_list.length; ++j) {
const related_item = full_list[j];
if (item == related_item) {
pattern = [item]
continue;
}
pattern.push(related_item);
if (pattern in pre_report) {
++pre_report[pattern];
} else {
pre_report[pattern] = 1;
}
}
}
// filter out only single occurring patterns
var report = {};
for (key in pre_report) {
if (pre_report[key] > 1) {
report[key] = pre_report[key];
}
}
console.log(report);
produces:
{ 'dog,cat': 5, 'dog,cat,pig': 2, 'cat,pig': 2 }
Yesterday, I faced this interview question. Initially, it seemed pretty easy, at least logically.
But somehow, I could not make it work in JavaScript.
Here is a 2d array of student scores, student names might be repeated multiple times. If this is the case, sum up all the scores and divide by the number of occurrences to find the average, do the Math.floor if necessary.
var arr = [
["Bobby","87"],
["Charles","100"],
["Eric","65"],
["Charles","22"],
["Charles","37"],
["Eric","49"]]
So, Charles average score would be Math.floor((100+22+37)/3) = 53
And, for Eric it would be Math.floor((65+49)/2) = 57.
So highest average would be ["Bobby","87"].
So far what I have tried fruitlessly..
var op_arr = [];
arr.each(function(item) {
var sum = 0;
var itemCount = 1;
var checkFlag = isItemInArray(arr,item);
if(checkFlag) {
itemCount++;
sum += item[1];
}
});
function isItemInArray(array,item) {
for(let i = 0;i < array.length; i++) {
if(array[i][0] === item[0]) {
return array[i];
}
}
return false;
}
But this does not work.
Please help me and explain the logic.
I would use some code to convert the list into a hash map based on the fact that there can be multiple of the same student.
var arr = [
["Bobby","87"],
["Charles","100"],
["Eric","65"],
["Charles","22"],
["Charles","37"],
["Eric","49"]
];
var scores = {};
for (var i = 0; i < arr.length; i++) {
var student = arr[i];
if (!scores.hasOwnProperty(student[0]))
scores[student[0]] = []
scores[student[0]].push(student[1])
}
The result should be:
{
"Bobby": ["87"],
"Charles": ["100", "22", "37"],
"Eric": ["65", "49"]
}
And now you can do a second pass over the object to calculate the average
for (var key in scores) {
if (!scores.hasOwnProperty(key)) continue;
var total = scores[key].reduce(function(next, cur) {
return next + parseInt(cur);
}, 0);
scores[key] = Math.floor(total / scores[key].length);
}
console.log(scores);
I'm sure you can make it a lot more elegant using ES6 features, but this should give you an idea of one solution.
You could first get all average values of each person and then get the highest average of the temporary result.
If more than one person have the same score, all persons are included.
var array = [["Bobby", "87"], ["Charles", "100"], ["Eric", "65"], ["Charles", "22"], ["Charles", "37"], ["Eric", "49"]],
highest = array
.reduce(function (r, a) {
var i = r.findIndex(b => a[0] === b[0]);
if (i !== -1) {
r[i][1] = (r[i][1] * r[i][2] + +a[1]) / ++r[i][2];
} else {
r.push(a.concat(1));
}
return r;
}, [])
.reduce(function (r, a, i) {
if (!i || r[0][1] < a[1]) {
return [a.slice(0, 2)];
}
if (r[0][1] === a[1]) {
r.push(a.slice(0, 2));
}
return r;
}, []);
console.log(highest);
You can following this approach
Collate all the scores for a name in an array of scores using reduce
Iterate the result using map and compute the average of scores array using reduce again.
Demo
var fnSumAvgArray = (arr) => arr.reduce( ( a, c ) => a + c, 0 )/arr.length;
var arr = [
["Bobby","87"],
["Charles","100"],
["Eric","65"],
["Charles","22"],
["Charles","37"],
["Eric","49"]];
var output = Object.values( arr.reduce( (a,c) => (
a[c[0]] = (a[c[0]] || { name : c[0], scores : [] }), //check if accumulator already has been initialized for this name or else intialize
a[ c[ 0 ] ].scores.push ( +c[1] ), //push the score into the name based score array
a ) , {}) ) //return the accumulator
.map( s => (
s.avg = fnSumAvgArray(s.scores), s
));
console.log( output );
var arr = [
["Bobby","87"],
["Charles","100"],
["Eric","65"],
["Charles","22"],
["Charles","37"],
["Eric","49"]];
var x, students=[], counter=[], scoreSums=[], name, i=0;
for(x in arr) {
name = arr[x][0];
j = students.indexOf(name);
if(j < 0) {
students[i] = name;
counter[i] = 1;
scoreSums[i] = parseInt(arr[x][1]);
i++;
} else {
scoreSums[j] += parseInt(arr[x][1]);
counter[j]++;
}
}
var maxMean = 0, mean;
for(i=0; i<students.length; i++) {
mean = scoreSums[i] / counter[i];
if(mean > maxMean) {
name = students[i];
maxMean = mean;
}
}
document.getElementById('maxMean').innerHTML='Highest average is '+ name+' at '+maxMean;
console.log(name, maxMean);
<div id="maxMean"></div>
Here is a 'round the houses' approach. It is a slightly longer way to write it out but I think it's human readable which should help with the concept.
I've commented the code below.
var arr = [
["Bobby", "87"],
["Charles", "100"],
["Eric", "65"],
["Charles", "22"],
["Charles", "37"],
["Eric", "49"]
];
let objects = [];
let names = [];
for (let item of arr) {
// Get unique names
if (names.indexOf(item[0]) <= 0) {
names.push(item[0]);
}
// Create object rather than array
let score = {
name: item[0],
score: parseInt(item[1])
}
objects.push(score);
}
// Work out average based on name
function countScore(name) {
let count = 0;
let total = 0;
for (let object of objects) {
if (object.name === name) {
count += 1;
total += object.score
}
}
let avgScore = total / count;
console.log(name + ': ' + avgScore)
}
// Run function for each unique name
for (let name of names) {
countScore(name);
}
I hope you find this helpful.
let findMaxAvg = (arr) => {
let studmap = new Map();
arr.forEach((item,ind) => {
let [name,score] = [item[0],item[1]];
!studmap.has(name) ? studmap.set(name,[score,1]) : studmap.set(name,
[studmap.get(name)[0]+score,studmap.get(name)[1]+1])
});
return Math.max(...[...studmap.values()].map(sdata =>
Math.round(sdata[0]/sdata[1])));
}
var arr = [
["Bobby","87"],
["Charles","100"],
["Eric","200"],
["Charles","22"],
["Charles","37"],
["Eric","49"]];
console.log(findMaxAvg(arr));
There's a lot of ways you can solve this as per the solutions provided above. I've also encountered this question before and I used
Map()
to collect all students and their scores and return array with [name, average].
const scores = [['brian', 80], ['brian', 90], ['brian', 65], ['robhy', 85], ['robhy', 75], ['ryan', 75], ['murphy', 85]];
const calculateHigestAvg = (scores)=>{
let studentObj = new Map();
let score = [];
for (var i = 0; i < scores.length; i++) {
if (studentObj.has(scores[i][0])) {
const studentScoreArr = studentObj.get(scores[i][0])
studentScoreArr.push(scores[i][1]);
studentObj.set(scores[i][0], studentScoreArr);
} else {
studentObj.set(scores[i][0], [scores[i][1]]);
}
}
return Array.from(studentObj.entries())
.map((item, index)=>{
return [item[0], Math.floor(item[1].reduce((a, b) => a + b) / item[1].length)]
}
, 0)
}
calculateHigestAvg(scores);
// results
[["brian", 78],["robhy", 80],["ryan", 75],["murphy", 85]]