【问题标题】:How can I order these json objects using LINQ?如何使用 LINQ 订购这些 json 对象?
【发布时间】:2016-03-04 20:12:32
【问题描述】:

有了这样的数据(作为旁注,json 文件中的这些单独的组件被正确地称为什么 - 记录?项目?元素?对象?jsonpiecesparts?):

[
  {
    "WeekOfAssignment": "2016-04-04T00:00:00",
    "TalkType": 1,
    "StudentID_FK": 32,
    "AssistantID_FK": 0,
    "CounselPoint": 10,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-11T00:00:00",
    "TalkType": 1,
    "StudentID_FK": 43,
    "AssistantID_FK": 0,
    "CounselPoint": 39,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-11T00:00:00",
    "TalkType": 2,
    "StudentID_FK": 44,
    "AssistantID_FK": 40,
    "CounselPoint": 12,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-11T00:00:00",
    "TalkType": 4,
    "StudentID_FK": 4,
    "AssistantID_FK": 24,
    "CounselPoint": 23,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-11T00:00:00",
    "TalkType": 6,
    "StudentID_FK": 1,
    "AssistantID_FK": 41,
    "CounselPoint": 42,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-25T00:00:00",
    "TalkType": 1,
    "StudentID_FK": 19,
    "AssistantID_FK": 0,
    "CounselPoint": 2,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-25T00:00:00",
    "TalkType": 2,
    "StudentID_FK": 13,
    "AssistantID_FK": 12,
    "CounselPoint": 41,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-25T00:00:00",
    "TalkType": 4,
    "StudentID_FK": 20,
    "AssistantID_FK": 42,
    "CounselPoint": 11,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  },
  {
    "WeekOfAssignment": "2016-04-25T00:00:00",
    "TalkType": 6,
    "StudentID_FK": 36,
    "AssistantID_FK": 39,
    "CounselPoint": 31,
    "HasBeenEmailed": false,
    "SlipHasBeenPrinted": false
  }
]

...我想根据 StudentID_FK 与 AssistantID_FK 一起工作的次数来对这些对象(或它们所称的任何东西)进行排序,其中将两者一起分配的次数最少位于列表顶部。 IOW,从未一起分配的两个应该是有序列表中的第一项,而那些一起分配次数最多的应该在底部。

注意:曾经显示为“StudentID_FK”的人下一次可能会显示为“AssistantID_FK”,反之亦然。

要确定这一点,需要查询整个 json 文件。我相信我可以得到这样的计数(假设 studentlist 是一个学生列表,如 json 文件中所示):

int countOfTimesWorkedTogether = GetCountOfTimesWorkedTogether(student1, student2);

private int GetCountOfTimesWorkedTogether(int student, int assistant)
{
    int StudentAndAssistant = studentlist.Where(s => s.StudentID_FK == student && a => a.AssistantID_FK == assistant).Count();
    int AssistantAndStudent = studentlist.Where(s => s.StudentID_FK == assistant && a => a.AssistantID_FK == student).Count();
    return StudentAndAssistant + AssistantAndStudent;
}

...但是我将如何在列表的 LINQ 查询中使用它?计数不是学生类的成员,那么可以使用什么“技巧”根据这些计数对结果进行排序?

我需要的是来自同一个列表的两个并排排序。第一个由 WeekOfAssignment 排序,“旁边”包含其他人的列表,按他们与当前在第一个查询填充的选中列表框中突出显示的人一起工作的最少次数排序。

在伪代码中,我最终需要的是:

List<Student> orderedStudents = studentlist.OrderBy(WeekOfAssignment);

这会填充 checklistboxStudents。

然后这个:

List<Student> orderedAssistants = studentlist.OrderBy(CountOfTimesWorkedWithSelectedPersonInChecklistboxStudents);

...填充checklistboxAssistants;因此,每次在 checklistboxStudents 中选择不同的学生时,都必须重新运行第二个查询,并对 checklistboxAssistants 重新排序。

更新

恐怕我没有解释清楚这个问题的真正本质。它是:

每周有许多 (3) 项作业,每人需要两名学生:“那个”学生和一名助理。

每个学生都可以扮演任何角色;他们可以讲 2、3、4、5、6 或 7。偶数是学生作业,奇数是助理作业。每个列表都是唯一的 - 那些接下来会被分配到 #2 的列表,接下来会被分配到 #3 的列表,等等。

因此,这六个列表中的每一个都是学生超集列表的唯一子集,在任何给定的一周内,每个列表中的总人数大约为 1/6(他们在下周移至下一个列表)。

所以我需要将列表 3 中的内容与列表 2 中的内容进行比较,将列表 5 中的内容与列表 4 中的内容进行比较,将列表 7 中的内容与列表 6 中的内容进行比较。

有一个学生列表,用于跟踪学生数据,例如给出的最后一个作业(#/talk 类型和日期)等。作业列表(上面显示的摘录)包含每个作业/周,“那个”学生的学生 (StudentID_FK),以及那个时候的助理学生 (AssistantID_FK)。

所以,重新考虑一下,我也许可以像这样(伪代码)命令助手:

0) 向 Student 类添加一个“OptionalOrderingVal”成员:

public int OptionalOrderingVal { get; set; }

首先,根据下一个任务类型获取初始助手列表,按最后任务日期排序;例如,对于作业#3,它支持#2:

var RVAssistantsList = studentlist.Where(t => t.talkType == 3).OrderBy(w => weekOfLastAssignment);

接下来,创建该子列表的新版本,填充 OptionalOrderingVal 成员:

int countOfAssignmentsWithStudent = 0;
int StudentId = comboboxRVStudent.SelectedValue;
foreach (Student assistant in RVAssistantsList)
{
    countOfAssignmentsWithStudent = assignmentsList.Where(StudentID_FK == StudentId && AssistantID_FK == AssistantId || StudentID_FK == AssistantId && AssistantID_FK == StudentId);
    assistant.OptionalOrderingVal = countOfAssignmentsWithStudent;
}

然后,按新值对助手列表进行排序:

RVAssistantsList = RVAssistantsList
    .OrderBy(o => o.OptionalOrderingVal)
    .ThenBy(w => w.WeekOfLastAssignment)
    .ToList();

最后,将该列表分配给适当的复选框:

checklistboxRVAssistants.Items = RVAssistantsList;

我可以称之为 kludgelent 或 eligy; YMMV。

【问题讨论】:

  • 我认为您的计数需要处理student == assistant 假设这是可能的情况。
  • 不,学生和助手永远不会在一个对象中相同。
  • 如果两个没有分配在一起,你想如何查找?我的意思是名单是由那些已经被分配在一起的学生组成的,可能还有其他一些没有被分配的学生,因此他们的名字不在名单中。
  • @user3473830:所有下一个作业是作为相关谈话类型的助理的学生都在第二个复选框中,按上次作业的时间排序;如果他们还没有,他们将在列表的顶部。然后,当在第一个(学生)复选框中选择了一个学生时,我想根据他们被分配与学生一起工作的时间来重新排序助手复选框的内容。

标签: c# json linq generic-list


【解决方案1】:

如果你有这样的模型:

public class Student
{
    public DateTime WeekOfAssignment { get; set; }
    public int TalkType { get; set; }
    public int StudentID_FK { get; set; }
    public int AssistantID_FK { get; set; }
    public int CounselPoint { get; set; }
    public bool HasBeenEmailed { get; set; }
    public bool SlipHasBeenPrinted { get; set; }
}

您可以使用匿名类型按属性 StudentID_FKAssistantID_FK 进行 linq 查询分组,并根据其大小对该组进行排序,然后再次连接元素。

这会根据StudentID_FKAssistantID_FK 在列表中出现的次数对元素进行排序,次数最少的优先:

var result = studentlist.GroupBy(m => new 
                                { 
                                    A = Math.Min(m.StudentID_FK, m.AssistantID_FK),  // This gets pairs like (1, 2) and (2, 1)
                                    B = Math.Max(m.StudentID_FK, m.AssistantID_FK)   // as the same (1, 2)
                                })
                    .OrderBy(g => g.Count())
                    .SelectMany(g => g)
                    .ToList();

【讨论】:

  • 谢谢!请参阅上面我对数据库 cat 的注释。
【解决方案2】:

您想要的是按一对学生 ID 作为学生或助理出现的次数进行排序,反之亦然。因此:

        var orderedAssistants = studentlist
            .OrderBy(a => GetCountOfTimesWorkedTogether(studentlist, a.StudentID_FK, a.AssistantID_FK))
            .ToList();

使用

    private static int GetCountOfTimesWorkedTogether(IEnumerable<Student> studentList, int id1, int id2)
    {
        var count = studentList.Where(s => s.StudentID_FK == id1 && s.AssistantID_FK == id2 || s.StudentID_FK == id2 && s.AssistantID_FK == id1).Count();
        return count;
    }

但请注意,这可能是学生人数的二次方。您正在寻找的是两个学生何时以任何组合一起工作的计数 - 学生 + 助理,反之亦然。表示与顺序无关的组合的标准方法是HashSet&lt;T&gt;,其中T 将是用于表示学生ID 的类型。然后你可以使用HashSet&lt;T&gt;.CreateSetComparer() 按内容相等性对集合进行分组:

因此以下不应该是二次的:

        var orderedAssistants = studentlist
            .GroupBy(s => (new[] { s.AssistantID_FK, s.StudentID_FK }).ToSet(), HashSet<int>.CreateSetComparer())
            .OrderBy(g => g.Count())
            .SelectMany(g => g)
            .ToList();

使用

public static class EnumerableExtensions
{
    public static HashSet<T> ToSet<T>(this IEnumerable<T> source)
    {
        return new HashSet<T>(source);
    }
}

【讨论】:

  • 非常令人印象深刻;不过,我可能一两天内都无法检查出来。我的猜测是这两个答案都是正确的,所以我将进入我的罗宾汉模式并将分数分配给最少的猫。如果事实证明他的方式行不通(而你的方式行不通),我将推翻投票。谢谢!
  • @B.ClayShannon - 两个答案都应该有效。我的更通用,其他的专门用于可以订购的钥匙对。
猜你喜欢
  • 2018-01-31
  • 1970-01-01
  • 2013-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-25
  • 2015-07-19
  • 1970-01-01
相关资源
最近更新 更多