【问题标题】:Sort by name and then group by some boolean relationship? (.NET)按名称排序,然后按某种布尔关系分组? (。网)
【发布时间】:2013-09-21 16:23:28
【问题描述】:

我有一个命名对象列表:

class NamedObject {
    public string name;
    public int value;
    public NamedObject(string name, int value) {
        this.name = name;
        this.value = value;
    }
}

...

public static bool HasRelationship(NamedObject a, NamedObject b) {
    return a.value == b.value;
}

...

var objs = new List<NamedObject>();
objs.Add(new NamedObject("D", 1));
objs.Add(new NamedObject("Z", 2));
objs.Add(new NamedObject("Y", 3));
objs.Add(new NamedObject("A", 2));
objs.Add(new NamedObject("C", 1));
objs.Add(new NamedObject("Z", 1));

我想按名称排序,然后按布尔关系进行子排序。在本示例中,布尔关系为 a.value == b.value

输出列表:

  • 一(2)
  • Z (2)
  • C (1)
  • D (1)
  • Z (1)
  • 是 (3)

所以按名称排序,按布尔关系分组,按名称排序子组。

编辑:

以上是对实际排序的简化,在我的应用程序中,HasRelationship 函数确定两个方向是否对称。方向被命名,以便它们在编辑器界面中按逻辑顺序显示。

这是一个可视化:

http://pbrd.co/16okFxp

【问题讨论】:

    标签: c# .net arrays linq sorting


    【解决方案1】:

    我对这个问题感到困惑,我会尽量说清楚。

    首先,您似乎想按名称对 NamedObjects 进行排序,这是清晰易懂的部分。订单应该完成这项工作。

    然后您想通过基于一对 NamedObjects 的任意谓词对其进行分区。我认为这是引起混乱的问题。

    您提供的谓词决定了 NamedObjects 对的属性,所以现在您正在处理对。这个问题没有唯一的答案。

    我了解您希望通过谓词对对进行分区,但您必须了解,使用布尔分区您将只有两个分区(关系为真或不正确),并且不保证分区内的值顺序。

    所以你最多可以得到(按对的第一个词的名称排序):

    • 对(A,Z)(真)
    • 对(A,C)(假)
    • 对(A,D)(假)
    • ...
    • 对(C,D)(真)
    • ...

    关键是如果不隐式处理对,就不能按对关系排序。所以为了给你一个答案,我假设:

    • 关系可能不是对称的
    • 您想按第一对术语名称排序

    在这种情况下,答案可能是。首先得到对。

    var namedPairs = namedObjects.SelectMany(outerNamedObject =>
        namedObjects.Select(innerNamedObject => new
            {
                First = outerNamedObject,
                Second = innerNamedObject
            }));
    

    然后我们进行分组

    var partitionedNamedPairs = namedPairs.GroupBy(pair => 
        HasRelationship(pair.First, pair.Second));
    

    然后,按第一个术语名称排序,然后按组键(关系分区)

    var result = partitionedNamedPairs.SelectMany(
             grouping => grouping.Select(pair => new { pair, key = grouping.Key }))
         .OrderBy(keyedPair => keyedPair.pair.First.name)
         .ThenBy(keyedPair => keyedPair.key);
    

    然后您可以使用 select 删除该对的第二项,但我不明白这一点,因为您提供的谓词是二进制的。

    【讨论】:

    • 对不起,我的问题令人困惑。我已经发布了一个看起来像是 hacky 解决方案的工作示例:hastebin.com/jepinidule.cs 我将尝试您在此处发布的内容,因为我还不明白。干杯:)
    【解决方案2】:

    我认为你应该自己加入你的列表,因为你的 HasRelationship 方法需要两个对象。

    var result = objs.OrderBy(x => x.name)
                .Join(objs, _ => true, _ => true, (l, r) => new { l, r, rel = HasRelationship(l, r) })
                .Where(x => x.rel)
                .SelectMany(x=>new []{x.l,x.r})
                .Distinct()
                .ToList();
    

    虽然这会返回您期望的列表,但我不能说我清楚地了解您的要求。

    【讨论】:

    • 确实,您的解决方案似乎确实提供了正确的输出!我已经发布了一个完整评论的答案,我希望它可以更好地理解我的问题。但是,由于您的解决方案有效且更简洁,因此认为它已被接受 :) 干杯!
    【解决方案3】:

    以下解决方案已被充分评论,希望能帮助该问题的未来读者了解所需的排序过程。

    @QtX 的回答简洁明了,尽管人们似乎很难理解我的实际要求,对那些人深表歉意!

    用法示例:

    var sortedObjs = objs.SortAndGroupByRelationship(obj => obj.name, HasRelationship);
    

    排序和分组的扩展方法:

    public static IEnumerable<T> SortAndGroupByRelationship<T, TKey>(this IEnumerable<T> objs, Func<T, TKey> keySelector, Func<T, T, bool> relationship) where TKey : IComparable<TKey> {
        // Group items which are related.
        var groups = new List<List<T>>();
        foreach (var obj in objs) {
            bool grouped = false;
    
            // Attempt to place named object into an existing group.
            foreach (var group in groups)
                if (relationship(obj, group[0])) {
                    group.Add(obj);
                    grouped = true;
                    break;
                }
    
            // Create new group for named object.
            if (!grouped) {
                var newGroup = new List<T>();
                newGroup.Add(obj);
                groups.Add(newGroup);
            }
        }
    
        // Sort objects within each group by name.
        foreach (var group in groups)
            group.Sort( (a, b) => keySelector(a).CompareTo(keySelector(b)) );
    
        // Sort groups by name.
        groups.Sort( (a, b) => keySelector(a[0]).CompareTo(keySelector(b[0])) );
    
        // Flatten groups into resulting array.
        var sortedList = new List<T>();
        foreach (var group in groups)
            sortedList.AddRange(group);
        return sortedList;
    }
    

    【讨论】:

      【解决方案4】:
      var sorted = objs.GroupBy(x => x.value, (k, g) => g.OrderBy(x => x.name))
                       .OrderBy(g => g.First().name)
                       .SelectMany(g => g);
      

      不使用HasRelationship 方法,准确返回您想要的。

      【讨论】:

      • HasRelationship 方法对我的具体应用很重要。虽然身份关系是这个问题的一个很好的简化。
      猜你喜欢
      • 2018-04-15
      • 1970-01-01
      • 1970-01-01
      • 2021-09-30
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 2013-01-14
      • 2011-11-22
      相关资源
      最近更新 更多