Reputation: 413
I have an array of objects (coming from a ajax/PDO call), like this:
signincounts: [{ status: 1
count: 3
teamid: 4
},{
status: 0
count: 1
teamid: 2
},{
status: 0
count: 2
teamid: 4
}]
These object represent the number of users signin in for a particular event:
In addition, I have the following information:
teamcount : {
2: 5
4: 6
}
I now want to sort this in Javascript to display the signin stats like this:
team 2: signed in/ signed out / not yet decided
team 4: signed in/ signed out / not yet decided
In this case that would be:
team 2: 0 / 1 / 4
team 4: 3 / 2 / 1
The difficulties:
I was thinking I need to sort it, but am unsure on how to approach this because of undefined zero counts and non-sequential teamids. My initial plan was to use two empty arrays (signin_count, signout_count), then loop over the ajax JSON and push the counts in there (and unshift in case it's the player team). However, how can I keep the teamid reference and undefined values in there (so indices remain "parallel")? And I'm assuming there's a cleaner & better way anyway (on the array-of-objects level itself), that'd allow me to sort it straight into the result string.
Tried searching as well, but didn't find anything for the double-sort-while-considering-specific-key/value-pairs problem.
Would be glad for some pointers.
Upvotes: 2
Views: 1841
Reputation: 1161
I think lodash would serve you well here.
var signincounts = [{
status: 1,
count: 3,
teamid: 4
},{
status: 0,
count: 1,
teamid: 2
},{
status: 0,
count: 2,
teamid: 4
}],
teamcount = {
2: 5,
4: 6
};
var result = _.chain(signincounts)
.groupBy('teamid')
.mapValues(mapTeams).value();
console.log(result);
// helper function for mapping from grouped data
function mapTeams(teamData, teamid) {
var team = {};
team.undecided = teamcount[teamid];
var signed_in = _.find(teamData, {status: 1}), // gets the object that tells us how many are signed in
signed_out = _.find(teamData, {status: 0}); // gets the object that tells us how many are signed out
team.signed_in = (signed_in && signed_in.count) ? signed_in.count : 0; // guard against undefined
team.signed_out = (signed_out && signed_out.count) ? signed_out.count : 0; // guard against undefined
team.undecided -= (team.signed_in + team.signed_out);
return team;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.2/lodash.min.js"></script>
So here's what's happening. First we call _.chain(signincounts)
. That means that we are going to take our signincounts and pass it through a pipeline of functions that will each modify the result from the previous function. So after signaling that we are going to chain, we group all the objects in the array by their teamid
. We get the following object:
{ '2': [ { status: 0, count: 1, teamid: 2 } ],
'4':
[ { status: 1, count: 3, teamid: 4 },
{ status: 0, count: 2, teamid: 4 } ] }
Because we're chaining, this is what's passed to mapValues
. mapValues
creates an new object with the same keys as the object passed in, but with different values, according to the callback function passed in. So our result will be an object that has the teamids as keys, which is what I believe you want. The helper function defines what will be mapped to each teamid. This helper function is executed for each key-value pair.
Inside the helper function, we create an object that will be our new teamid mapping. So for each teamid, we are going to return an object that looks like { signed_in: <Number>, signed_out: <Number>, undecided: <Number> }
. We set undecided as the total number on the team. Then we find the object in the array from the previous result that tells us how much are attending for that team, and then we find out how many are not attending. We assign signed_in
and signed_out
fields accordingly. Then we deduct from our total amount how many are signed_in and signed_out to get our undecided count.
Upvotes: 1
Reputation: 215029
Two steps:
first, iterate teamcount
and create an object teamid => [0, 0, count]
. In other words, assume all team members undecided
then, iterate signincounts
and do two things: add c.count
to a respective slot of result[teamid]
and subtract c.count
from result[teamid][undecided]
This gives you the stats you want, final sorting and output are left to the reader.
Code:
data = {
signincounts: [{
status: 1,
count: 3,
teamid: 4
}, {
status: 0,
count: 1,
teamid: 2
}, {
status: 0,
count: 2,
teamid: 4
}]
,
teamcount: {
2: 5,
4: 6
}
};
res = {}
Object.keys(data.teamcount).forEach(teamid =>
res[teamid] = [0, 0, data.teamcount[teamid]]);
data.signincounts.forEach(c => {
res[c.teamid][c.status] = c.count;
res[c.teamid][2] -= c.count;
});
console.log(res)
Upvotes: 1
Reputation: 77089
Ok then! Let's do this in 2 phases:
First, we must eliminate entries that should not be printed. You mentioned counts that equal 0
:
data = data.filter(function(entry) { entry.count > 0 })
Now that all the remaining entries in data
are to be printed, we can sort them into the desired order. You mentioned two constraints:
The .sort()
method of Javascript Array
objects is called with a comparison function with this signature:
function compare(a, b) returns Number
The returned Number
is interpreted as follows:
compare
returns < 0
, a
goes before b
compare
returns > 0
, b
goes before a
compare
returns 0
, it doesn't matter (usually respects previous order)So, let's write a compare
function that can sort your data:
function compare(a, b) {
// The special USER_TEAM_ID goes before any other element:
if (a.teamId === USER_TEAM_ID) return -1
// For any other case:
return a.teamId - b.teamId // invert these to reverse the sort
}
Now you can call:
data.sort(compare)
Upvotes: 2