2013-09-21 74 views
-1

我有一個名爲對象的列表:由布爾關係按名稱排序然後按布爾關係分組? (.NET)

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

輸出列表:

  • A(2)
  • Z(2)
  • C(1)
  • d(1)
  • Z(1)
  • Y(3)

所以按名稱排序,按布爾關係分組,按名稱排序子組。

編輯:

以上是實際排序的簡化,在我的申請的HasRelationship函數確定兩個方向是否有對稱性。方向被命名,以便它們在編輯器界面中以邏輯順序出現。

這裏是一個可視化:

http://pbrd.co/16okFxp

回答

1

我想你應該自己加入你的清單,因爲你的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(); 

雖然這會返回您期望的列表,但我不能說我明白您的要求。

+0

事實上,你的解決方案似乎確實提供了正確的輸出!我發佈了一個完整評論的答案,我希望能更好地理解我的問題。但是,由於您的解決方案有效並且更簡潔,因此請考慮接受:)乾杯! –

0
var sorted = objs.GroupBy(x => x.value, (k, g) => g.OrderBy(x => x.name)) 
       .OrderBy(g => g.First().name) 
       .SelectMany(g => g); 

返回正是你想要的東西,而無需使用HasRelationship方法。

+0

的'HasRelationship'方法是用於我的特定的應用非常重要。雖然身份關係對於這個問題是一個很好的簡化。 –

2

我對這個問題感到困惑,我會盡量做到儘可能清楚。

首先看來你想按名稱對NamedObjects進行排序,這是清晰和容易的部分。應該由一個命令做這項工作。

然後你想通過基於一對NamedObjects的任意謂詞來區分它。我認爲這是造成混亂的問題。

您提供的謂詞決定了NamedObjects對的屬性,所以現在您正在處理對。這個問題沒有獨特的答案。

我知道你想用謂詞對對進行分區,但是你必須明白,對於一個布爾分區,你只有兩個分區(關係是對的或不對),並且在分區內沒有保證值的順序。

所以頂多可能會導致你(通過名稱上對第一項排序):(A,Z)

  • 對(真)
  • 對(A,C)(假)
  • pair(A,D)(false)
  • ...
  • 對(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刪除這一對的第二項,但我沒有看到這一點,因爲你提供的謂詞是二元的。

+0

對不起,我的問題很混亂。我已經發布了一個感覺像一個hacky解決方案的工作示例:http://hastebin.com/jepinidule.cs我將試驗你在這裏發佈的內容,因爲我還沒有理解它。歡呼:) –

1

以下解決方案已完全評論,希望能夠幫助未來的讀者解決此問題,以瞭解所需的排序過程。

@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; 
}