【问题标题】:How do I sort a list based on a given sequence of one field?如何根据一个字段的给定序列对列表进行排序?
【发布时间】:2020-06-03 03:52:18
【问题描述】:

我有一个从数据库中读取的列表,类似于:

var managers = _repo.Where(xxxx).ToList();

managers 的数据在 json 中显示为

[
    {
        "id":"b2071b2d-3d1a-4403-b044-0a59514c4431",
        "name":"Lucy",
        "mobile":"xxx"
    },
    {
        "id":"639c108f-ec00-4fcd-814a-c3859930b1c1",
        "name":"Franck",
        "mobile":"xxx2"
    },
    {
        "id":"943b2ad4-8ef0-4cf7-824e-de3a7837a1cd",
        "name":"Jerry",
        "mobile":"xxx3"
    }
]

现在我需要按给定的名称序列重新排序这个列表,例如:

    var orderByName = _svc.GetManagerSortOrder(); // ["Jerry","Lucy","Franck"]

现在我正在使用foreach 来执行此操作,编码太多了。有没有更好的办法?

这是我目前的解决方案:

var orderByName = new List<string>() { "Franck", "Jerry" };
var sortedManagers = new List<Manager>();
foreach(string orderName in orderByName) // loop, and sort manager list based on a given name list
{
    var manager = managers.Where(a => a.name == orderName).FirstOrDefault();
    if (manager == null)
        continue;
    sortedManagers.Add(manager);
}

if(sortedManagers.Count != managers.Count) // if counts not equal, there is someone not on the name list, so these men need to be appended to the sortedManagers list
{
    var sortedManagersId = sortedManagers.Select(s => s.id);
    var managersNotInclude = managers.Where(a => !sortedManagersId.Contains(a.id)).ToList();
    sortedManagers = sortedManagers.Concat(managersNotInclude).ToList();
}

【问题讨论】:

标签: c# .net linq sorting


【解决方案1】:

不幸的是,如果不是用于处理源列表中的名称,那么以下答案将对您有所帮助,因为 IndexOf() 为未发现的元素返回 -1 后,目标列表中不存在这些名称:Sort a list from another list IDs

您需要做的是编写一个实现ICompare 的客户Comparer。这可能看起来像这样:

class DependentComparer<T> : IComparer<T>
{
    // Backing field to contain the order dependent list
    List<T> lookUpTable;

    public DependentComparer(List<T> lookUpTable)
    {
        this.lookUpTable = lookUpTable;
    }

    public int Compare(T x, T y)
    {
        // Determine the index of the compared elements in the dependent list
        int xIndex = lookUpTable.IndexOf(x);
        int yIndex = lookUpTable.IndexOf(y);

        // If neither were in the dependent list, they are equal
        if ((xIndex == -1) && (yIndex == -1)) return 0;

        // If only the y was found, then y is greater than the x element
        if (xIndex == -1) return 1;
        // If only the x was found, then y is less than the x element
        if (yIndex == -1) return -1;

        // If both were found, then return the delta of their indicies
        return xIndex - yIndex;
    }
}

这可以用作:

var orderByName = new List<string>() { "Frank", "Jerry" };
var managersNames = new List<string>() { "Jerry", "Frank", "Lucy" };

managersNames.Sort(new DependentComparer<string>(orderByName));

输出:Frank, Jerry, Lucy

注意:此方法不会对lookUpTable 中未出现的元素进行排序操作。它们将按照它们在源中的顺序附加到结果的末尾。此外,给出的示例好像列表属于string,而不是manager。转换很简单,但如果您需要修改,请告诉我。

【讨论】:

  • 谢谢乔治。我从没想过ICompare 可以用于此。您和 Harald 的建议对我很有帮助!
【解决方案2】:

所以你有一个经理序列,每个经理都有一个名字;并且您有一系列名称,例如 ["Jerry","Lucy","Franck"]。

您想订购经理,这样您首先将所有经理命名为“Jerry”,然后将所有经理命名为“Lucy”,等等。您希望以不在您的姓名序列中的经理结束。

考虑创建一个实现IComparer&lt;Manager&gt; 的对象:获得两个Manager,并决定谁应该先去。比如:一个Manger叫“Lucy”,另一个叫“Jerry”,所以“Jerry”应该先bo。

用法如下:

IEnumerable<Manager> unorderedManagers = ...
IComparer<Manager> managerComparer = ...

IEnumerable<Manager> orderedManagers = unorderedManagers.OrderBy(managerComparer);

所以让我们创建一个 ManagerComparer 类。

class ManagerComparer : IComparer<Manager>
{
    public ManagerComparer(IEnumerable<string> names)
    {
    }

    public int Compare(Manager x, Manager y) {...}
    // TODO implement
}

如果您有两个经理作为输入:x 和 y,那么您首先需要带有“Jerry”的经理。如果没有“杰瑞”,则首先要“露西”,等等。

为了提高效率,我们将所有名称放在字典中:键是名称,值是索引。所以“Jerry”的索引为 0,“Lucy”的索引为 1,等等

你得到经理 X 和 Y:得到他们的名字,从字典中得到索引。索引最低的排在第一位。

听起来是个好主意?让我们开始吧:

private readonly IDictionary<string, int> managerNames;

public ManagerComparer(IEnumerable<string> names)
{
    // TODO: what to do if no names?

    this.managerNames = names.Select( (name, i) => new
    {
        Name = name,
        Index = i,
    })
    .ToDictionary(item => item.Name, item => item.Index);
}

比较方法。

当您有两个经理时,我们知道该怎么做。比较字典中名称的索引。根据值返回 -1 / 0 / +1。这可以通过if/then/else来完成,使用现有的IComparer&lt;int&gt;更容易:

private static readonly IComparer<int> indexComparer = Comparer<int>.Default;


public int Compare(Manager x, Manager y)
{
    // TODO: decide what to return if x or y null: first or last in your result
    if (x == null)
    {
        if (y == null)
           return 0;
        else
            return ... // -1 or +1;
    }
    else if (y == null)
        return ... // -1 or +1;

    // if here, both x and y not null
    string nameX = x.Name;
    string nameY = y.Name;

    int indexX = this.managerNames[nameX];
    int indexY = this.managerNames[nameY];

    // the one with the lowest index comes first:
    return indexComparer.Compare(indexX, indexY);
}

再次使用:

IEnumerable<Manager> unorderedManagers = ...
IEnumerable<string> orderByNames = new sting[] {"Jerry", "Lucy", ...}
IComparer<Manager> managerComparer = new ManagerComparer(orderByNames);

IEnumerable<Manager> orderedManagers = unorderedManagers.OrderBy(managerComparer);

【讨论】:

  • 恭敬地,除了删除泛型(可以说使其不那么可重用)之外,我看不出这个答案除了我提供的之外还有什么。如果您觉得我的回答不足,请更公平地发表评论和请求编辑/扩展,而不是发布扩展副本。
  • 当我开始编辑时,你的答案还没有出现。你想让我删除我的答案吗?
  • 不,谢谢,有疑问。几个小时后,这似乎很奇怪。这是一个非常完整的答案,无疑会对人们有所帮助。
  • 我在下班休息时开始回答。几个小时后,在我下一次休息时,我继续
  • 对我来说这是一个非常详细的分步指南。并且对我帮助很大。我以前从未想过扩展 IComparer。谢谢!
【解决方案3】:

Array.Sort 提供了一种非常简洁快速的方法,它根据包含规定顺序的给定数组的顺序对数组进行排序。

var managers = JsonConvert.DeserializeObject<IEnumerable<Manager>>(json).ToArray();
var order = new List<string> { "Jerry", "Lucy", "Franck" };
var indexes = managers
    .Select(m => order.IndexOf(m.Name))
    .Select(i => i == -1 ? int.MaxValue : i)
    .ToArray();

Array.Sort(indexes, managers);

managers 包含 5 个名字时,数组indexes 看起来类似于[1,2147483647,2,2147483647,0],给定的名字分别位于位置 1、3 和 5(Jerry 在 5,获取索引 0 等)。

Array.Sort“排列”两个数组并根据indexes 中的值对排列进行排序。结果为managers,按照规定的顺序排序。请注意,managers 中元素的物理顺序会持续更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 2022-11-16
    • 1970-01-01
    • 2023-02-08
    • 2018-11-15
    相关资源
    最近更新 更多