【问题标题】:Ordering list by a specific order特定订单的订购清单
【发布时间】:2016-03-29 14:32:20
【问题描述】:

我有一个List 并且我有一个新订单,其中List 应该有int[] 中的项目我希望List 中的项目应该按照@987654327 中的项目重新排序@。这是我的代码:

   class Program
    {
        static void Main(string[] args)
        {
            List<Test> tests = new List<Test>() { 
                new Test(){ No = 201 },
                new Test(){ No = 101 },
                new Test(){ No = 300 },
                new Test(){ No = 401 },
                new Test(){ No = 500 },
                new Test(){ No = 601 }
            };


            int[] newOrder = new int[6] { 201, 401, 300, 101, 601, 500 };

            //after the opration the List should contain items in order 201, 401, 300, 101, 601, 500

            List<Test> newTests = new List<Test>();

            foreach(var order in newOrder)
            {
                var item = tests.SingleOrDefault(t => t.No == order);

                if (item != null)
                    newTests.Add(item);
            }


        }

    }

这很好用。但它会创建一个单独的List 并对其执行操作。有没有更好的方法可以内置在.Net 操作中,或者可以在相同的List 上执行操作而不创建这些Temp List 等?

谢谢。

【问题讨论】:

  • 如果你想在同一个列表中做,那么根据顺序数组的索引开始交换原始列表中的元素。不会有默认机制
  • 您可以将您的阵列与您的列表联合起来,然后Select 没有排序。
  • @Unknown User 那不行,因为我们需要一个特定的比较逻辑来创建一个IComparer,这里好像没有这个系统
  • @MrinalKamboj 我认为交集可以解决问题。

标签: c# .net


【解决方案1】:

在运行这样的排序时,您需要考虑性能。

如果您只期望少数几个元素,那么Pedro's solution 是可以的。

如果您希望有很多元素(例如,100 个或 1000 个),那么在 tests 的整个集合中搜索 newOrder 中的每个元素并不是一个好主意。在这种情况下,对所有索引/排序顺序查找使用Dictionary 会很有帮助。试试这样的:

List<Test> tests = new List<Test>() { 
    new Test(){ No = 101 },
    new Test(){ No = 201 },
    new Test(){ No = 300 },
    new Test(){ No = 401 },
    new Test(){ No = 500 },
    new Test(){ No = 601 }
};


int[] newOrder = new int[6] { 201, 401, 300, 101, 601, 500 };

// Create a Dictionary/hashtable so we don't have to search in newOrder repeatedly
// It will look like this: { {201,0}, {401,1}, {300,2}, {101,3}, {601,4}, {500,5} }
Dictionary<int, int> newOrderIndexedMap = Enumerable.Range(0, newOrder.Length - 1).ToDictionary(r => newOrder[r], r => r);

// Order using 1 CPU
var orderedTests = tests.OrderBy(test => newOrderIndexedMap[test.No]);
// Order using multi-threading
var orderedInParallelTests = tests.AsParallel().OrderBy(test => newOrderIndexedMap[test.No]);

// Order using 1 CPU, when it's possible that a match will not be found in newOrder
var orderedTestsSafe = tests.OrderBy(test => 
    {
        int index;
        bool foundIndex = newOrderIndexedMap.TryGetValue(test.No, out index);
        return foundIndex ? index : Int32.MaxValue;
    });

请注意,此答案和 Pedro 都假设 newOrder 包含 tests 元素中包含的所有值,反之亦然。

【讨论】:

  • 您可能希望通过使用默认值将数字放在末尾来处理列表和数组值之间的不匹配。假设它们总是相同在实际情况下可能会失败,事实上,如果 List 有更多的元素,它会失败
  • 如果newOrder 中可能缺少某些元素,那么@MrinalKamboj 的建议是正确的。在这种情况下,您应该使用 orderedTestsSafe 之类的东西。
  • 使用索引图很好,但是由于Int32.MaxValue,任何与新订单列表不匹配的项目将被随机排序,但OP可能不需要。好答案
【解决方案2】:
var newTesties= newOrder.Select(o => tests.First(t => t.No == o));

基本上,我选择 newOrder 中的每个数字“o”,并使用它来选择相应的测试。不过,您最终会得到一个新列表。

【讨论】:

    【解决方案3】:

    使用左连接可以是使用自定义排序的一种方式。对于任何与自定义排序不匹配的项目,请使用列表的原始顺序,从末尾开始

    var results = from a in tests.Select((r, i) => new {item = r, Index = i})
                  // left join
                  from b in newOrder.Select((r, i) => new { item = r, Index = i })
                                    .Where(b => a.item.No == b.item).DefaultIfEmpty() 
                  // Not in order list then use original ordering
                  orderby (b == null ? tests.Count() + a.Index : b.Index) 
                  select a.item;
    

    .Net Fiddle

    【讨论】:

    • 左外连接,瞧,这处理所有场景
    【解决方案4】:

    尝试像这样加入你的数组和列表

    newOrder.Join(tests, no => no, tst => tst.No, (no, tst) => tst)
    

    【讨论】:

    • 非常好的解决方案,因为它可以通过加入来工作,但是当原始列表包含不属于数组的数字时,Left Outer Join 会是更好的选择
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多