Sort data in objects/arrays on multiple keys - javascript

I have a multiplayer game and the gameplay data is stored like this:
var gameplays = [
{id: "1", player1: "bob", player2: "tim", score1: 2, score2: 14},
{id: "2", player1: "bob", player2: "tim", score1: 7, score2: 3},
{id: "3", player1: "bob", player2: "tim", score1: 6, score2: 10},
{id: "4", player1: "bob", player2: "tim", score1: 5, score2: 1}
];
What is the most efficient way to find the top 5 highscores from all the games by searching "score1" and "score2" and output them like this:
HIGHSCORES
1. Tim - 14
2. Tim - 10
3. Bob - 7
4. Bob - 6
5. Bob - 5

var scores = [];
for (var i = 0; i < gameplays.length; i++) {
scores.push({score: gameplays[i].score1, name: gameplays[i].player1});
scores.push({score: gameplays[i].score2, name: gameplays[i].player2});
}
scores.sort(function (a, b) {
return b.score - a.score;
});
scores.splice(0, 5);
First, get the scores and flatten them in a scores array along with score and name of individual.
Then, we sort the array and splicing will get the top 5 scores with name.

const gameplays = [
{id: "1", player1: "bob", player2: "tim", score1: 2, score2: 14},
{id: "2", player1: "bob", player2: "tim", score1: 7, score2: 3},
{id: "3", player1: "bob", player2: "tim", score1: 6, score2: 10},
{id: "4", player1: "bob", player2: "tim", score1: 5, score2: 1}
];
First, write all relevant game information into an array of objects, each of which contain a player key corresponding to the player's name and a score key, which corresponds to the score:
const results = [];
gameplays.forEach(game => {
for(let i = 1; i <= 2; i++) {
results.push({});
results[results.length - 1].player = `${game[`player${i}`].slice(0, 1).toUpperCase()}${game[`player${i}`].slice(1).toLowerCase()}`;
results[results.length - 1].score = game[`score${i}`];
}
});
Then, sort the array in descending order of scores before only keeping the top 5 with slice.
const topFive = results.sort((result1, result2) => result2.score - result1.score)
.slice(0, 5);
Finally, display the top 5 scores.
console.log('High Scores');
for(let i = 0; i < topFive.length; i++) {
console.log(`${i + 1}. ${topFive[i].player} - ${topFive[i].score}`);
}

You can do it in a sort pretty easily I think with:
gameplays.sort(function(_a, _b){
var a = _a.score1 > _a.score2 ? _a.score1 : _a.score2;
var b = _b.score1 > _b.score2 ? _b.score1 : _b.score2;
if(a < b) {
return 1;
}
if(a > b) {
return -1;
}
return 0;
})
Then, you you can access the top five with:
gameplays.slice(0, 5)

If you'r going to benchmark this I would be interested in the performance of the "functional" kind of solution with Ramda.
var gameplays = [
{id: "1", player1: "bob", player2: "tim", score1: 2, score2: 14},
{id: "2", player1: "bob", player2: "tim", score1: 7, score2: 3},
{id: "3", player1: "bob", player2: "tim", score1: 6, score2: 10},
{id: "4", player1: "bob", player2: "tim", score1: 5, score2: 1}
];
// find the 5 top highscores regardless which player
const normalizeScore = ({id, player1, score1, player2, score2}) =>
[{id, player1, score: score1}, {id, player2, score: score2}];
const sortByScore = (play1, play2) => play2.score - play1.score;
const normalizeGameplays = R.chain(normalizeScore); // chain === flatMap
const sortGameplays = R.sort(sortByScore);
const topFive = R.slice(0, 5);
const highscore = R.compose(topFive, sortGameplays, normalizeGameplays);
console.log(highscore(gameplays));
#See: https://jsbin.com/wixowu/edit?html,js,console

Related

Get first array objects in every second arrays index

Get first array objects in every second arrays index . Is it possible to get like this given expected output or not possible, we can use for or any other method in JavaScript This is the Two arrays:
let First = [
{lesson: 1},
{lesson: 2},
{lesson: 3}
]
# let Second = [
{
name: "John",
age : 26,
lesson: 1
},
{
name: "Ken",
age : 40,
lesson: 2
},
{
name: "William",
age : 18,
lesson: 3
}
]
Expected output
{
name: "John",
age : 26,
lesson: 1,
lesson: 2,
lesson: 3
},
{
name: "Ken",
age : 40,
lesson: 1,
lesson: 2,
lesson: 3
},
{
name: "William",
age : 18,
lesson: 1,
lesson: 2,
lesson: 3
},
]
you can't have multiple key with différent value in a object but you can do that if you want :
const persons = [{
name: "John",
age : 26,
lesson: [1, 2, 3],
},
{
name: "Ken",
age : 40,
lesson: [1, 2, 3],
},
{
name: "William",
age : 18,
lesson: [1, 2, 3],
},
]
And with code :
for (let i = 0; i < second.length; i++) {
second[i]["lesson"] = [1, 2, 3]
}

How to calculate and create new object value from two different arrays in Javascript

var array1 = [{issueCount: 16, failCount: 38, id: 1},
{issueCount: 15, failCount: 37, id: 2},
{issueCount: 15, failCount: 34, id: 3}];
var array2 = [{id: 1, totalAttempts: 57},
{id: 2, totalAttempts: 59},
{id: 3, totalAttempts: 67},
{id: 4, totalAttempts: 59}];
I have two arrays. From the above arrays, I need to calculate failure Percentage using the (array1. fail count/array2.totalAttempts) * 100 [id is common between two arrays]. And the final array wants in the below format.
outputArray = [{id: 1, issueCount: 16, failCount: 38, percentage: 66.66},
{id: 2, issueCount: 15, failCount: 37, percentage: 62.71},
{id: 3, issueCount: 15, failCount: 34, percentage: 50.74}];
Thanks in advance.
You can achieve this with a simple for loop.
Just check if the id exists in the second array, if so make your calculations.
const array1 = [{issueCount: 16, failCount: 38, id: 1},
{issueCount: 15, failCount: 37, id: 2},
{issueCount: 15, failCount: 34, id: 3}];
const array2 = [{id: 1, totalAttempts: 57},
{id: 2, totalAttempts: 59},
{id: 3, totalAttempts: 67},
{id: 4, totalAttempts: 59}];
const outputArray = [];
array1.forEach(i1 => {
const i2 = array2.find(i => i.id === i1.id);
if(i2) {
outputArray.push({
id: i1.id,
issueCount: i1.issueCount,
failCount: i1.failCount,
percentage: (i1.failCount / i2.totalAttempts) * 100
});
}
});
console.log(outputArray)
You can do:
const array1 = [{issueCount: 16, failCount: 38, id: 1},{issueCount: 15, failCount: 37, id: 2},{issueCount: 15, failCount: 34, id: 3}]
const array2 = [{id: 1, totalAttempts: 57},{id: 2, totalAttempts: 59},{id: 3, totalAttempts: 67},{id: 4, totalAttempts: 59}]
const mergedArrays = Object.values([...array1, ...array2].reduce((a, c) => (a[c.id] = { ...a[c.id], ...c }, a), {}))
const outputArray = mergedArrays
.filter(o => o.issueCount && o.totalAttempts)
.map(({ id, issueCount, failCount, percentage, totalAttempts }) => ({
id,
issueCount,
failCount,
percentage: Math.round(failCount / totalAttempts * 100 * 100) / 100
}))
console.log(outputArray)
Thank you all for your posts. I have also find the solution below.
outputArray = [];
array1.forEach(function(dataItem1, idx) {
var array2Items = array2[idx];
var outputItems = {};
if (dataItem1 && array2Items){
if(dataItem1.id == array2Items.id){
outputItems.id = dataItem1.id;
outputItems.issueCount = dataItem1.issueCount;
outputItems.failCount = dataItem1.failCount;
outputItems.percentage = ((dataItem1.failCount/array2Items.totalAttempts)*100).toFixed(2);
outputArray.push(outputItems);
}
}
});
console.log(outputArray);

Summing Records and Consolidating Based on IDs in Array

I have an array of records that contain objects, each with an id and an amount. Now, in this array, some elements have the same id. What I need to do is iterate over this array, and first, sum up the values for records that have the same id, and then return just one record when that is the case - so I end up with an array with only unique ids -- each one also containing an amount property.
In other words, I want to take this array:
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
... and produce this array:
const transformedRecords = [
{id: 1, amount: 30},
{id: 2, amount: 10},
{id: 3, amount: 0}
];
I've thought about using a for-of loop for this, but that might bet pretty verbose, and I'm guessing there's a more succinct way to accomplish this - perhaps with reduce()? What's an elegant way to approach this, preferably using es6+ syntax?
Use Array.reduce, for each iteration, check if you have an object with the current id in the accumulator, if you do, add the amounts, if not, push the current object to the accumulator :
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10},
{id: 4, amount: -10},
{id: 4, amount: -10}
];
const result = records.reduce((acc, curr) => {
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) {
acc[ndx].amount += curr.amount
}
else{
acc.push(curr)
}
return acc;
}, [])
console.log(result)
You can use reduce() to create an object and then use map() on its entries to create array of objects back
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
const res = Object.entries(records.reduce((ac, a) => {
ac[a.id] = (a[a.id] || 0) + a.amount;
return ac
}, {})).map(([id, amount]) => ({id, amount: amount < 0 ? 0 : amount}))
console.log(res)

d3js: How to select only x number of values from data

I have some data in the format given below:
var data = [
{name: "A", rank: 0, c: 92},
{name: "B", rank: 45, c: 99},
{name: "C", rank: 89, c: 89},
{name: "D", rank: 23, c: 99},
{name: "E", rank: 56, c: 98}
];
I want to find a subset of this data like say only 3 values. I am not sure how to do this is d3js. The solutions I found were mainly based on the property of data like:
data = data.filter(function(d){return +d.rank < 10});
But this gives me all data points with rank < 10 but I want to limit the "size" of data (number of data points) based on some user input.
How can I do that?
May be like this using filter + slice:
var data = [
{name: "A", rank: 0, c: 92},
{name: "B", rank: 45, c: 99},
{name: "C", rank: 89, c: 89},
{name: "D", rank: 23, c: 99},
{name: "E", rank: 56, c: 98}
];
var size = 3; //user input size that is needed
var result = data.filter(function(d) {return (+d.rank > 10)}).slice(0,size)
console.log(result)

lodash method to merge 2 objects of different sizes

Is there any method to merge 2 arrays of objects like this
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}];
var b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}]
//final result should be
c = [
{id:1, val: 1},
{id:21, val: 21},
{id:2, val: 2},
{id:22, val: 22},
{id:3, val: 3},
{id:23, val: 23},
{id:4, val: 4},
{id:5, val: 5}
]
offcourse I can create it by myself, but just want to check whether lodash provide it or not
You could first zip the arrays, flatten the result and then use compact to remove the missing array elements (zip adds them as undefined):
var c = _.compact(_.flatten(_.zip(a,b)))
Or using chaining:
var c = _(a)
.zip(b)
.flatten()
.compact()
.value()
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}];
var b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}]
var c = _(a)
.zip(b)
.flatten()
.compact()
.value()
document.getElementById('results').textContent = JSON.stringify(c);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<pre id="results"></pre>
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}];
var b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}];
// loop through the biggest array and reduce the result (no need for the value we just need the accumulator and the index)
var result = _.reduce(a.length < b.length? b: a, function(res, _, i) {
if(i < a.length) res.push(a[i]); // if there is still elements in a, push the current one
if(i < b.length) res.push(b[i]); // if there is still elements in b, push the current one
return res;
}, []);
console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
In plain Javascript, you could use a function which iterates to the minimum length of both, assembles the values and concat the rest at the end.
function insert(a, b) {
var c = [],
i = 0,
l = Math.min(a.length, b.length);
while (i < l) {
c.push(a[i], b[i]);
i++;
}
return c.concat(a.slice(i), b.slice(i));
}
var a = [{ id: 1, val: 1 }, { id: 2, val: 2 }, { id: 3, val: 3 }, { id: 4, val: 4 }, { id: 5, val: 5 }],
b = [{ id: 21, val: 21 }, { id: 22, val: 22 }, { id: 23, val: 23 }];
console.log(insert(a, b));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ecmascript5 solution using Math.max()(to find the larger array size) and Array.prototype.push() functions:
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}],
b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}],
maxLen = Math.max(a.length, b.length), aLen = a.length, bLen = b.length,
maxList = aLen > bLen ? a : b;
result = [];
for (var i = 0; i < maxLen; i++) {
(i < aLen && i < bLen) ? result.push(a[i], b[i]) : result.push(maxList[i]);
}
console.log(result);

Categories

Resources