订购对象并根据自定义优先级进行选择

时间:2018-01-21 22:49:25

标签: c# linq sql-order-by

我有一个自定义优先级数组。

int[] priorities = { 866, 663, 855, 853, 854};

我现在要使用此代码对列表进行排序,并根据标准选择第一个。

            var target = (from people in GetNearbyPeopleList()
                          where people.DistanceToTravel > 0 && people.ReachedDestination == false
                          orderby //not sure what to do here
                          select people).FirstOrDefault();

所以我想按照我的自定义优先顺序排序。其中people.currentlocation由我的优先级数组排序。

我该做什么 - ?

orderby priorities.Contains(people.currentlocation)

这就是我能想到的全部,但它并没有按照我的自定义优先顺序排列。我希望它完全遵循这个顺序:

int[] priorities = { 866, 663, 855, 853, 854};

因此,如果位置是866,请选择那个。我只想选择一个,我想根据该优先级列表选择第一个。如果currentlocation == 866不存在,则选择663等等。

4 个答案:

答案 0 :(得分:1)

如果你的值在编译时是固定的......避免使用数组并写:

orderby
  people.currentLocation == 866 ? 1 :
  people.currentLocation == 663 ? 2 :
  people.currentLocation == 855 ? 3 :
  people.currentLocation == 853 ? 4 :
  people.currentLocation == 854 ? 5 :
  6

如果您的值在运行时更改,但值的数量有一些固定的最大值,则写入:

Person FindPriorityPerson(IQueryable<Person> query,
  int p1 = 0, int p2 = 0, int p3 = 0, int p4 = 0,
  int p5 = 0, int p6 = 0, int p7 = 0, int p8 = 0)
{
  return query.OrderBy(person =>
    person.currentLocation == p1 ? 1 :
    person.currentLocation == p2 ? 2 :
    person.currentLocation == p3 ? 3 :
    person.currentLocation == p4 ? 4 :
    person.currentLocation == p5 ? 5 :
    person.currentLocation == p6 ? 6 :
    person.currentLocation == p7 ? 7 :
    person.currentLocation == p8 ? 8 :
    9).FirstOrDefault();
}

答案 1 :(得分:0)

我认为这样做会。这使您可以根据数组顺序进行排序。

必须添加逻辑,所以-1(未找到)在最后

CREATE TABLE movie(
  id INT(1) NOT NULL AUTO_INCREMENT,
  nearname VARCHAR(25) NOT NULL,
  release_date DATE NOT NULL,
  lang VARCHAR(10) NOT NULL,
  PRIMARY KEY(id),
  CONSTRAINT same_movie FOREIGN KEY(id) REFERENCES movie_cast(movie_id)
);

CREATE TABLE movie_cast(
  movie_id INT(1) NOT NULL AUTO_INCREMENT,
  director_name VARCHAR(20) NOT NULL,
  actor_name VARCHAR(20) NOT NULL,
  actress_name VARCHAR(20) NOT NULL,
  PRIMARY KEY(movie_id),
  CONSTRAINT same_movie FOREIGN KEY(movie_id) REFERENCES movie(id)
);

答案 2 :(得分:0)

这不可能通过这种方式完成。 * Linq无法以您需要的方式使用您自己的阵列。但是我可以想到其他几种方法

  • 重新设计您的应用程序,以便您在服务器上的表格中具有优先级
  • 使用表格值参数( TVP )。这是将数组发送到服务器以在查询中使用的正确方法,它可以毫无问题地扩展到数千个项目。它需要支持该服务器的服务器(如Microsoft SQL服务器),并且查询必须在linq之外完成。 Linq不支持这一点。
  • 使用 Contains 并查询所有项目,在客户端上选择第一项。 Contains可以使用您自己的数组,但请注意它将其转换为参数,一个值表示一个参数。如果数组中的项目太多(> 100),请不要使用它,否则查询会很慢,并且会因超过约2000项而崩溃(取决于服务器类型)。
  • 循环数组并一次查询一个项目。您可以将其与Contains结合并一次查询10个项目,例如加快速度。

* Amy B发现了一种方式,我很糟糕。但要注意限制。它不是每个值都使用一个,而是两个参数,使其更加受限制。它可能会导致表扫描,这可能会更昂贵,取决于表大小和数组大小。在20M表上,它需要大约25000倍的数据库资源,而不仅仅是使用Contains查询所有5行。您应该将它与Contains过滤器结合使用以避免表扫描,但这意味着每个项目有三个参数......

答案 3 :(得分:0)

Ctznkan525's回答让我想到了使用"Select with index"按顺序或优先顺序对数组进行排序。

所以你希望所有优先考虑的人都在列表或优先级中。第二个是列表中第二个被提及的优先级的人,第三个是列表中优先级为第三的人等。

您是否注意到我提到了识别订单的索引?因此,如果我将索引添加到您的优先级列表中,我可以按此索引进行排序。

如果GetNearByPeopleList返回IEnumerable,则此方法有效。

var indexedPriorities = priorities.Select( (priority, index) => new
{
    Priority = priority,
    OrderIndex = index,
});
var result = GetNearbyPeopleList()
    .Where(...)                          // take only people you want
    .Join(indexedPriorities,             // join with indexedPriorities
    people => people.CurrentLocation,    // from each people take the CurrentLocation
    indexedPrio => indexedPrio.Priority, // from each indexedPriority take the priority
    (people, prio) => new                // when they match, make a new object
    {
        Index = prio.Index,              // containing the index of the matching priority
        People = people,                 // and the matching data
    })
    .OrderBy(item => item.Index)         // order by ascending index
    .Select(item => item.People);        // keep only the People

此代码不包含CurrentLocation中不在您的优先级列表中的所有人。如果你想要它们,你应该把它们Concat到你想要的地方,可能最后。

唉,linq-to-entities does not support "select with index"。您可以尝试先添加索引,将列表设为AsQueryable,然后执行join:

var indexedPriorities =  = priorities.Select( (priority, index) => new
{
    Priority = priority,
    OrderIndex = index,
})
.AsQueryable();

var result = GetNearbyPeopleList()
    .Where(...)       
    .Join(indexedPriorities, 
    people => people.CurrentLocation, CurrentLocation
    indexedPrio => indexedPrio.Priority, 
    ...

唉这不起作用,因为你只能传输简单类型AsQueryable以用于连接。

但是,如果数据是远程的,并且您确实需要这个,那么您可以将优先级和索引作为小数转移:

  var indexedPriorities =  = priorities.Select( (x, i) => (decimal)x + (decimal)i/1000M)
      .AsQueryable();

小数点前的数字是优先级,小数点后是排序索引:(。001之前是.002等

IQueryable<People> nearbyPeople = GetNearbyPeopleList()
   .Where(...);

var result = nearbyPeople.Join(indexedPriorities,
    people => people.CurrentLocation,
    prio => (int)Math.Floor(prio),
    (people, prio) => new
    {
        OrderIndex =(prio - Math.Truncate(prio),
        People = people,
    })
    .OrderBy(item => item.OrderIndex)
    .Select(item => item.People);

首先,我将原始优先级列表:{866,663,855,853,854}更改为{866.000,663.001,855.002,853.003等}。假设您的优先级列表没有1000个元素。

然后我使用866.000,663.001等Math.Floor进行连接。匹配时,我将小数点后的部分保存为OrderIndex:.000,.001,.002等(不需要,但如果需要:乘以1000)

订购后我摆脱了OrderIndex。