在键上的对象的Javascript排序数组

时间:2022-09-28 16:02:59

I have an array of objects (coming from a ajax/PDO call), like this:

我有一个对象数组(来自ajax/PDO调用),如下所示:

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:

这些对象表示为特定事件登录的用户数量:

  • status = 1 (signed in, will attend)
  • 状态= 1(签到,参加)
  • status = 0 (signed out, will not attend)
  • 状态= 0(签署,不参加)
  • count = x number of people signing in/out
  • count =签到/退出的人数
  • teamid = y the team they belong to
  • 他们所属的队。

In addition, I have the following information:

此外,我还有以下信息:

teamcount : {
    2: 5
    4: 6
}
  • teamid : total number of players
  • teamid:玩家的总数

I now want to sort this in Javascript to display the signin stats like this:

现在我想用Javascript对其进行排序,以显示如下的登录状态:

team 2: signed in/ signed out / not yet decided

第二组:签到/签到/尚未决定

team 4: signed in/ signed out / not yet decided

第4组:签到/签到/尚未决定

In this case that would be:

在这种情况下,这将是:

team 2: 0 / 1 / 4

第二组:0 / 1 / 4

team 4: 3 / 2 / 1

团队4:3 / 2 / 1

The difficulties:

困难:

  • teamids are unique, but not necessarily sequential
  • teamid是唯一的,但不一定是顺序的。
  • if the count for a status is zero, it is not part of the returned JSON
  • 如果状态的计数为零,则它不是返回的JSON的一部分
  • objects are not necessarily in order
  • 对象不一定是有序的
  • there can anywhere from two to dozens of teams
  • 可以有两到几十个团队
  • The string output should be sorted by teamid, with the team of the viewer at the top (there's an additional variable, playerteamid, which can be used to simply check against)
  • 字符串输出应该按照teamid进行排序,查看器团队位于顶部(还有一个额外的变量playerteamid,可用于简单地检查)

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.

我认为我需要对它进行排序,但是由于没有定义的零计数和非连续的teamid,我不确定如何处理这个问题。我最初的计划是使用两个空数组(signin_count、signout_count),然后对ajax JSON进行循环,并在其中推入计数(如果是玩家团队,则取消移位)。但是,如何在其中保留teamid引用和未定义的值(因此索引仍然是“并行的”)?我假设有一种更简洁、更好的方法(在对象数组本身级别上),这样我就可以直接将它排序到结果字符串中。

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.

我很高兴能给你一些建议。

3 个解决方案

#1


1  

Two steps:

两个步骤:

  • first, iterate teamcount and create an object teamid => [0, 0, count]. In other words, assume all team members undecided

    首先,迭代teamcount并创建一个对象teamid => [0,0, count]。换句话说,假设所有的团队成员都还没有决定

  • 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]

    然后,迭代signincounts并做两件事:添加c。数到相应的结果槽,然后减去c。从结果数[teamid](未定)

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)

#2


2  

Ok then! Let's do this in 2 phases:

那好吧!我们分两个阶段来做:

Filtering phase

First, we must eliminate entries that should not be printed. You mentioned counts that equal 0:

首先,我们必须消除不应该打印的条目。你提到的计数等于0:

data = data.filter(function(entry) { entry.count > 0 })

Sorting phase

Now that all the remaining entries in data are to be printed, we can sort them into the desired order. You mentioned two constraints:

现在数据中剩下的所有项都要打印出来,我们可以将它们排序为所需的顺序。你提到的两个约束条件:

  • Lower team IDs should appear first
  • 先出现较低的团队id
  • A particular team ID should always appear at the top
  • 一个特定的团队ID应该总是出现在顶部

The .sort() method of Javascript Array objects is called with a comparison function with this signature:

Javascript数组对象的.sort()方法通过与此签名的比较函数调用:

function compare(a, b) returns Number

The returned Number is interpreted as follows:

返回的数字解释如下:

  • If compare returns < 0, a goes before b
  • 如果比较返回< 0,a在b之前
  • If compare returns > 0, b goes before a
  • 如果比较返回> 0,b在a之前
  • If compare returns 0, it doesn't matter (usually respects previous order)
  • 如果比较返回0,则没有关系(通常是考虑到先前的顺序)

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)

#3


1  

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:

这是发生了什么。首先,我们调用_.chain(signincounts)。这意味着我们将使用我们的signincounts并将它传递给一个函数管道,每个函数都将修改前面函数的结果。在发出信号后,我们将数组中的所有对象分组。我们得到以下对象:

{ '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.

因为我们要进行链接,这就是传递给mapValues的东西。根据传入的回调函数,mapValues使用与传入的对象相同的键,但使用不同的值创建一个新的对象。因此,我们的结果将是一个具有teamid作为键的对象,这就是我相信您想要的。helper函数定义了将映射到每个teamid的内容。这个助手函数为每个键值对执行。

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.

在helper函数中,我们创建一个对象,该对象将是新的teamid映射。因此,对于每个teamid,我们将返回一个对象,它看起来像{signed_in: , signed_out: , undecided: }。我们把球队的总人数定为未定。然后我们从之前的结果中找到数组中的对象,这个结果告诉我们这个团队有多少人参与,然后我们发现有多少人没有参与。我们相应地分配signed_in和signed_out字段。然后我们从总数中减去有多少signed_in和signed_out,以得到未确定的计数。

#1


1  

Two steps:

两个步骤:

  • first, iterate teamcount and create an object teamid => [0, 0, count]. In other words, assume all team members undecided

    首先,迭代teamcount并创建一个对象teamid => [0,0, count]。换句话说,假设所有的团队成员都还没有决定

  • 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]

    然后,迭代signincounts并做两件事:添加c。数到相应的结果槽,然后减去c。从结果数[teamid](未定)

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)

#2


2  

Ok then! Let's do this in 2 phases:

那好吧!我们分两个阶段来做:

Filtering phase

First, we must eliminate entries that should not be printed. You mentioned counts that equal 0:

首先,我们必须消除不应该打印的条目。你提到的计数等于0:

data = data.filter(function(entry) { entry.count > 0 })

Sorting phase

Now that all the remaining entries in data are to be printed, we can sort them into the desired order. You mentioned two constraints:

现在数据中剩下的所有项都要打印出来,我们可以将它们排序为所需的顺序。你提到的两个约束条件:

  • Lower team IDs should appear first
  • 先出现较低的团队id
  • A particular team ID should always appear at the top
  • 一个特定的团队ID应该总是出现在顶部

The .sort() method of Javascript Array objects is called with a comparison function with this signature:

Javascript数组对象的.sort()方法通过与此签名的比较函数调用:

function compare(a, b) returns Number

The returned Number is interpreted as follows:

返回的数字解释如下:

  • If compare returns < 0, a goes before b
  • 如果比较返回< 0,a在b之前
  • If compare returns > 0, b goes before a
  • 如果比较返回> 0,b在a之前
  • If compare returns 0, it doesn't matter (usually respects previous order)
  • 如果比较返回0,则没有关系(通常是考虑到先前的顺序)

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)

#3


1  

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:

这是发生了什么。首先,我们调用_.chain(signincounts)。这意味着我们将使用我们的signincounts并将它传递给一个函数管道,每个函数都将修改前面函数的结果。在发出信号后,我们将数组中的所有对象分组。我们得到以下对象:

{ '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.

因为我们要进行链接,这就是传递给mapValues的东西。根据传入的回调函数,mapValues使用与传入的对象相同的键,但使用不同的值创建一个新的对象。因此,我们的结果将是一个具有teamid作为键的对象,这就是我相信您想要的。helper函数定义了将映射到每个teamid的内容。这个助手函数为每个键值对执行。

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.

在helper函数中,我们创建一个对象,该对象将是新的teamid映射。因此,对于每个teamid,我们将返回一个对象,它看起来像{signed_in: , signed_out: , undecided: }。我们把球队的总人数定为未定。然后我们从之前的结果中找到数组中的对象,这个结果告诉我们这个团队有多少人参与,然后我们发现有多少人没有参与。我们相应地分配signed_in和signed_out字段。然后我们从总数中减去有多少signed_in和signed_out,以得到未确定的计数。