C# 在Linq中按子集合中的最小值对父集合排序

C# 在Linq中按子集合中的最小值对父集合排序,c#,linq,C#,Linq,在平局的情况下,这并不能解释第二(或第三等)小的原因 考虑到这3位家长的孩子年龄相对应,我希望他们按照这个顺序出现 P1 1,2,7 P2 1,3,6 P3 1,4,5 您可以使用并带走第二个和第三个孩子。但是,这是不可伸缩的,因此它取决于impl的需要 如果您想要更健壮的东西,可以执行以下操作。它将适用于此特定情况。不过,我要看看是否可以将其优化为更通用:) 公共静态类myExt { 公共静态列表OrderByWithTieBreaker(此列表父级,int-depth=0) { 如果(深度

在平局的情况下,这并不能解释第二(或第三等)小的原因

考虑到这3位家长的孩子年龄相对应,我希望他们按照这个顺序出现

  • P1 1,2,7
  • P2 1,3,6
  • P3 1,4,5
    • 您可以使用并带走第二个和第三个孩子。但是,这是不可伸缩的,因此它取决于impl的需要

      如果您想要更健壮的东西,可以执行以下操作。它将适用于此特定情况。不过,我要看看是否可以将其优化为更通用:)

      公共静态类myExt
      {
      公共静态列表OrderByWithTieBreaker(此列表父级,int-depth=0)
      {
      如果(深度>父项[0]。子项.Count())
      返回父母;
      var returnedList=新列表();
      Func keySelector=x=>
      {
      IEnumerable enumerable=x.Children.OrderBy(y=>y.Age).Skip(depth);
      如果(!enumerable.Any())
      返回0;//如果没有孩子,则返回可能的最低年龄
      返回enumerable.Min(z=>z.Age);
      };
      var orderedParents=parents.OrderBy(keySelector);
      var groupings=orderedParents.GroupBy(keySelector);
      foreach(分组中的var分组)
      {
      if(grouping.Count()>1)
      {
      var innerOrder=grouping.ToList().OrderByWithTieBreaker(深度+1);
      returnedList=returnedList.Union(innerOrder.ToList();
      }
      其他的
      returnedList.Add(grouping.First());
      }
      返回列表;
      }
      }
      [测试夹具]
      公共类TestClass
      {
      公共类父级{public string Name{get;set;}公共列表子级{get;set;}
      公共类子{public int Age{get;set;}}
      [测试]
      public void TestName()
      {
      var parents=新列表
      {
      新父项{Name=“P3”,子项=新列表{new Child{Age=1},新子项{Age=3},新子项{Age=6},新子项{Age=7},
      新父项{Name=“P4”,子项=新列表{新子项{Age=1},新子项{Age=3},新子项{Age=6},新子项{Age=7},
      新父项{Name=“P2”,子项=新列表{new Child{Age=1},新子项{Age=3},新子项{Age=6}},
      新父项{Name=“P1”,子项=新列表{new Child{Age=1},新子项{Age=2},新子项{Age=7}},
      新父项{Name=“P5”,子项=新列表{新子项{Age=1},新子项{Age=4},新子项{Age=5}}
      };
      var f=parents.OrderByWithTieBreaker();
      整数计数=1;
      foreach(f中的变量d)
      {
      断言(d.Name,Is.EqualTo(“P”+count));
      计数++;
      }
      }
      
      您需要编写类似以下扩展方法的内容:

      public static class myExt
        {
          public static List<Parent> OrderByWithTieBreaker(this List<Parent> parents, int depth = 0)
          {
            if (depth > parents[0].Children.Count())
              return parents;
            var returnedList = new List<Parent>();
      
            Func<Parent, int> keySelector = x =>
          {
            IEnumerable<Child> enumerable = x.Children.OrderBy(y => y.Age).Skip(depth);
            if (!enumerable.Any())
              return 0; //If no children left, then return lowest possible age
            return enumerable.Min(z => z.Age);
          };
            var orderedParents = parents.OrderBy(keySelector);
            var groupings = orderedParents.GroupBy(keySelector);
            foreach (var grouping in groupings)
            {
              if (grouping.Count() > 1)
              {
                var innerOrder = grouping.ToList().OrderByWithTieBreaker(depth + 1);
                returnedList = returnedList.Union(innerOrder).ToList();
              }
              else
                returnedList.Add(grouping.First());
            }
            return returnedList;
          }
        }
        [TestFixture]
        public class TestClass
        {
          public class Parent { public string Name { get; set; } public List<Child> Children { get; set; } }
          public class Child { public int Age {get;set;} }
      
          [Test]
          public void TestName()
          {
            var parents = new List<Parent>
              {
                new Parent{Name="P3", Children = new List<Child>{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}, new Child{Age=7}}},
                new Parent{Name="P4", Children = new List<Child>{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}, new Child{Age=7}}},
                new Parent{Name="P2", Children = new List<Child>{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}}},
                new Parent{Name="P1", Children = new List<Child>{new Child{Age=1}, new Child{Age=2}, new Child{Age=7}}},
                new Parent{Name="P5", Children = new List<Child>{new Child{Age=1}, new Child{Age=4}, new Child{Age=5}}}
              };
            var f = parents.OrderByWithTieBreaker();
            int count = 1;
            foreach (var d in f)
            {
              Assert.That(d.Name, Is.EqualTo("P"+count));
              count++;
            }
          }
      
      一般实施:

      var orderedParents = parents.OrderBy(p => p.Children, c => c.Age);
      
      //
      ///给出了确定元素集合的方法(例如
      ///父母的子女)以及这些项目的可比财产
      ///(例如,孩子的年龄)这会对元素集合进行排序
      ///根据第一个元素属性的排序顺序
      ///它们各自的系列。如果出现平局,请退回到
      ///后续要素(视情况而定)。
      /// 
      公共静态IOrderedEnumerable OrderBy(this IEnumerable@this,Func getKeys,Func getValue)
      其中TValue:i可比较
      {
      return@this.OrderBy(x=>x,newkeycomarer(getKeys,getValue));
      }
      私有类密钥比较器:IComparer
      其中TValue:i可比较
      {
      私钥;
      私有函数GetValue;
      公钥比较器(Func-getKeys,Func-getValue)
      {
      this.GetKeys=GetKeys;
      this.GetValue=GetValue;
      }
      公共整数比较(TX,TY)
      {
      var xKeys=GetKeys(x).OrderBy(GetValue).Select(GetValue);
      var yKeys=GetKeys(y).OrderBy(GetValue).Select(GetValue);
      foreach(xKeys.Zip(yKeys,Tuple.Create)中的var对)
      {
      如果(对项目1.比较到(对项目2)!=0)
      返回pair.Item1.CompareTo(pair.Item2);
      }
      返回xKeys.Count().CompareTo(yKeys.Count());
      }
      }
      
      因此,在概念层面上,您要做的是比较两个序列。与其尝试对这个特定序列进行特殊处理,我们只需编写一个能够比较任意两个序列的比较器

      它将遍历序列中的项目,比较相同位置的项目,然后如果发现一对不相等的项目,它就会知道结果

      /// <summary>
      /// Given a way to determine a collection of elements (for example
      /// children of a parent) and a comparable property of those items
      /// (for example age of a child) this orders a collection of elements
      /// according to the sorting order of the property of the first element
      /// of their respective collections. In case of a tie, fall back to
      /// subsequent elements as appropriate.
      /// </summary>
      public static IOrderedEnumerable<T> OrderBy<T, TKey, TValue>(this IEnumerable<T> @this, Func<T, IEnumerable<TKey>> getKeys, Func<TKey, TValue> getValue)
          where TValue : IComparable<TValue>
      {
          return @this.OrderBy(x => x, new KeyComparer<T, TKey, TValue>(getKeys, getValue));
      }
      
      private class KeyComparer<T, TKey, TValue> : IComparer<T>
          where TValue : IComparable<TValue>
      {
          private Func<T, IEnumerable<TKey>> GetKeys;
          private Func<TKey, TValue> GetValue;
      
          public KeyComparer(Func<T, IEnumerable<TKey>> getKeys, Func<TKey, TValue> getValue)
          {
              this.GetKeys = getKeys;
              this.GetValue = getValue;
          }
      
          public int Compare(T x, T y)
          {
              var xKeys = GetKeys(x).OrderBy(GetValue).Select(GetValue);
              var yKeys = GetKeys(y).OrderBy(GetValue).Select(GetValue);
      
              foreach (var pair in xKeys.Zip(yKeys, Tuple.Create))
              {
                  if (pair.Item1.CompareTo(pair.Item2) != 0)
                      return pair.Item1.CompareTo(pair.Item2);
              }
      
              return xKeys.Count().CompareTo(yKeys.Count());
          }
      }
      

      以上内容已经在以下数据上进行了测试:
      A(4,1,2)B(2,4)C(2,2)D()
      排序为
      D A C B
      zip的良好使用。我在测试中检查了我能想到的所有边缘情况:)可能的重复。具体来说,您可以从那里使用实现作为
      比较
      方法:
      parents.OrderBy(p=>p.Children.Select(x=>x.Age).ToList(),new SequenceComparer())
      (其中
      SequenceComparer.Compare
      是该链接上的实现)+1,
      Compare
      函数非常简洁……我想序列长度不同的情况除外。@Rawling是的,你可以添加
      .DefaultIfEmpty(x.Count().CompareTo(y.Count()))
      就在where to cover之后,但这是两次完全迭代两个序列,这比需要做的工作多得多。唯一的替代方法是放弃整个LINQ解决方案,全部通过手动迭代完成,这是我想要避免的。
      var orderedParents = parents.OrderBy(p => p.Children, c => c.Age);
      
      /// <summary>
      /// Given a way to determine a collection of elements (for example
      /// children of a parent) and a comparable property of those items
      /// (for example age of a child) this orders a collection of elements
      /// according to the sorting order of the property of the first element
      /// of their respective collections. In case of a tie, fall back to
      /// subsequent elements as appropriate.
      /// </summary>
      public static IOrderedEnumerable<T> OrderBy<T, TKey, TValue>(this IEnumerable<T> @this, Func<T, IEnumerable<TKey>> getKeys, Func<TKey, TValue> getValue)
          where TValue : IComparable<TValue>
      {
          return @this.OrderBy(x => x, new KeyComparer<T, TKey, TValue>(getKeys, getValue));
      }
      
      private class KeyComparer<T, TKey, TValue> : IComparer<T>
          where TValue : IComparable<TValue>
      {
          private Func<T, IEnumerable<TKey>> GetKeys;
          private Func<TKey, TValue> GetValue;
      
          public KeyComparer(Func<T, IEnumerable<TKey>> getKeys, Func<TKey, TValue> getValue)
          {
              this.GetKeys = getKeys;
              this.GetValue = getValue;
          }
      
          public int Compare(T x, T y)
          {
              var xKeys = GetKeys(x).OrderBy(GetValue).Select(GetValue);
              var yKeys = GetKeys(y).OrderBy(GetValue).Select(GetValue);
      
              foreach (var pair in xKeys.Zip(yKeys, Tuple.Create))
              {
                  if (pair.Item1.CompareTo(pair.Item2) != 0)
                      return pair.Item1.CompareTo(pair.Item2);
              }
      
              return xKeys.Count().CompareTo(yKeys.Count());
          }
      }
      
      public class SequenceComparer<TSource> : IComparer<IEnumerable<TSource>>
      {
          private IComparer<TSource> comparer;
          public SequenceComparer(IComparer<TSource> comparer = null)
          {
              this.comparer = comparer ?? Comparer<TSource>.Default;
          }
          public int Compare(IEnumerable<TSource> x, IEnumerable<TSource> y)
          {
              return x.Zip(y, (a, b) => comparer.Compare(a, b))
                      .Where(n => n != 0)
                      .DefaultIfEmpty(x.Count().CompareTo(y.Count()))
                      .First();
          }
      }
      
      var query = parents.OrderBy(parent => parent.Children
          .OrderBy(child => child.Age)
          .Select(child => child.Age)
          , new SequenceComparer<int>());