从List <object>查找多个唯一匹配,其中两个条件必须不同

时间:2019-01-22 14:30:37

标签: c# algorithm list linq

我无法在基于两个字段(JOB_ID和EMPLOYEE_ID)的唯一列表中选择第一项。

每项工作应仅分配给一名雇员(最低工资为OVERALL_SCORE的雇员),然后继续分配下一名雇员。

列表对象如下:

JobMatch.cs

public int JOB_ID { get; set; }
public int JOB_MATCHES_COUNT { get; set; }

EmployeeMatch.cs

public int EMPLOYEE_ID { get; set; }
public int EMPLOYEE_MATCHES_COUNT { get; set; }

Rankings.cs

public int JOB_ID { get; set; }
public int EMPLOYEE_ID { get; set; }
public int TRAVEL_TIME_MINUTES { get; set; }
public int PRIORITY { get; set; }
public int OVERALL_SCORE { get; set; }
  

Rankings.cs根据旅行时间字段和   员工/工作的匹配数。

EmployeeMatch.cs

+-------------+-------------------+
| EMPLOYEE_ID | EMP_MATCHES_COUNT |
+-------------+-------------------+
|           3 |                 1 |
|           4 |                 1 |
|           2 |                 3 |
|           1 |                 4 |
+-------------+-------------------+

JobMatch.cs

+--------+-------------------+
| JOB_ID | JOB_MATCHES_COUNT |
+--------+-------------------+
|      1 |                 1 |
|      2 |                 2 |
|      3 |                 2 |
|      4 |                 4 |
+--------+-------------------+

Ranking.cs (简称为不填满屏幕)

+--------+-------------+---------------+
| JOB_ID | EMPLOYEE_ID | OVERALL_SCORE |
+--------+-------------+---------------+
|      4 |           3 |           800 |
|      4 |           4 |           800 |
|      3 |           1 |           800 |
|      3 |           2 |          1200 |
|      2 |           1 |          1600 |
|      2 |           2 |          1800 |
|      4 |           1 |          2000 |
|      4 |           2 |          2100 |
|      1 |           1 |          6400 |
+--------+-------------+---------------+

基本上,我们的想法是在此列表中选择第一个唯一的Employee和Job,然后将最佳匹配项放在一个单独的列表中,类似于上述情况的以下内容:

+--------+-------------+---------------+
| JOB_ID | EMPLOYEE_ID | OVERALL_SCORE |
+--------+-------------+---------------+
|      4 |           3 |           800 |
|      3 |           1 |           800 |
|      2 |           2 |          1800 |
+--------+-------------+---------------+

我尝试了以下操作,但未按预期工作:

var FirstOrder = (rankings.GroupBy(u => u.JOB_ID)
.Select(g => g.First())).ToList();

var SecondOrder = (FirstOrder.GroupBy(u => u.EMPLOYEE_ID)
.Select(g => g.First())).ToList(); 

3 个答案:

答案 0 :(得分:1)

想法是选择第一个元素,然后从列表中删除相应的元素,以确保下一个选择是唯一的,如下所示:

var rankings = new List<Rankings> {
    new Rankings{  JOB_ID= 4,EMPLOYEE_ID= 3, OVERALL_SCORE=  800 },
    new Rankings{  JOB_ID= 4,EMPLOYEE_ID= 4, OVERALL_SCORE=  800 },
    new Rankings{  JOB_ID= 3,EMPLOYEE_ID= 1, OVERALL_SCORE=  800 },
    new Rankings{  JOB_ID= 3,EMPLOYEE_ID= 2, OVERALL_SCORE= 1200 },
    new Rankings{  JOB_ID= 2,EMPLOYEE_ID= 1, OVERALL_SCORE= 1600 },
    new Rankings{  JOB_ID= 2,EMPLOYEE_ID= 2, OVERALL_SCORE= 1800 },
    new Rankings{  JOB_ID= 4,EMPLOYEE_ID= 1, OVERALL_SCORE= 2000 },
    new Rankings{  JOB_ID= 4,EMPLOYEE_ID= 2, OVERALL_SCORE= 2100 },
    new Rankings{  JOB_ID= 1,EMPLOYEE_ID= 1, OVERALL_SCORE= 6400 },
};
var cpy = new List<Rankings>(rankings);
var result = new List<Rankings>();
while (cpy.Count() > 0)
{
    var first = cpy.First();
    result.Add(first);
    cpy.RemoveAll(r => r.EMPLOYEE_ID == first.EMPLOYEE_ID || r.JOB_ID == first.JOB_ID);
}

结果:

+--------+-------------+---------------+
| JOB_ID | EMPLOYEE_ID | OVERALL_SCORE | 
+--------+-------------+---------------+
|      4 |           3 |           800 |
|      3 |           1 |           800 |   
|      2 |           2 |          1800 |
+--------+-------------+---------------+

答案 1 :(得分:1)

真的,如果您想获得该工作的最高分,则无需按唯一的JOB_ID/EMPLOYEE_ID进行选择,而需要按JOB_ID/OVERALL_SCORE进行排序,然后选择第一个每JOB_ID个匹配的员工(尚未在“分配的列表”中)。

您可以使用LINQ按顺序获取项目:

var sorted = new List<Ranking>
( 
  rankings
    .OrderBy( r => r.JOB_ID )
    .ThenBy( r => r.OVERALL_SCORE ) 
);

...然后剥离想要的员工...

  var best = new List<Ranking>( );
  sorted.ForEach( r1 => 
  {
    if ( !best.Any
    ( 
      r2 => 
        r1.JOB_ID == r2.JOB_ID 
        || 
        r1.EMPLOYEE_ID == r2.EMPLOYEE_ID
    ) )
    {
      best.Add( r1 );
    }
  } );

您可以在IComparable<Ranking>上实现Ranking,而不是使用Linq 生成排序列表,然后对排名进行排序:

public class Ranking : IComparable<Ranking>
{
  int IComparable<Ranking>.CompareTo( Ranking other )
  {
    var jobFirst = this.JOB_ID.CompareTo( other.JOB_ID );
    return
      jobFirst == 0?
        this.OVERALL_SCORE.CompareTo( other.OVERALL_SCORE ):
        jobFirst;
  } 

  //--> other stuff...

}

然后,当您对排名进行排序()时,它们将按JOB_ID / OVERALL_SCORE顺序排列。实现IComparable<Ranking>可能会更快,并且会占用更少的内存。

请注意,您有问题...可能是一个未声明的目标。找最多的工作更重要...还是为最多的员工找到工作更重要?我选择的路线会按照您的建议去做,只是随您去选择最适合该职位的员工...但是,也许,职位2的 only 员工可能与该职位的最佳员工相同工作1 ...如果您将他/她放到工作1,则可能没有其他人去工作2。这可能会变得很复杂:-)

答案 2 :(得分:0)

基本上,您可以使用System.Linq.Distinct方法,该方法由自定义相等比较器IEqualityComparer<Ranking>增强。 System.Linq开箱即用地提供了此方法。

public class Comparer : IEqualityComparer<Ranking>
{
    public bool Equals(Ranking l, Ranking r)
    {
        return l.JOB_ID == r.JOB_ID || l.EMPLOYEE_ID == r.EMPLOYEE_ID;
    }

    public int GetHashCode(Ranking obj)
    {
        return 1;
    }
}

这里的技巧是使用GetHashCode方法,然后像这样简单

rankings.Distinct(new Comparer())