2010-09-23 40 views
0

我有一個按元素的Name屬性排序的元素集合。我需要在維持訂單的同時在集合中插入一個新元素。我正在尋找一個簡潔的LINQ方式來做到這一點。我的代碼在下面。 「this.Children」是集合,「d」是我需要插入的新元素。它需要兩次通過集合才能找到插入點。有沒有辦法從First()擴展方法獲取索引? (請不要使用foreach,我知道:),我正在學習LINQ)。linq按順序插入元素的方法

謝謝! 康斯坦丁


var v = this.Children.FirstOrDefault(x => string.Compare(x.Name, d.Name) > 0); 
int index = this.Children.IndexOf(v); 

if (index < 0) 
{ 
    this.children.Add(d); 
} 
else 
{ 
    this.Children.Insert(index, d); 
} 

回答

6

是,使用overload of Select其中包括指數以及價值:

var pair = this.Children 
       .Select((value, index) => new { value, index }) 
       .FirstOrDefault(x => string.Compare(x.value.Name, d.Name) > 0); 

if (pair == null) 
{ 
    Children.Add(d); 
} 
else 
{ 
    Children.Insert(pair.index, d); 
} 

注意,這仍然是低效的,但 - 如果你已經知道的值進行排序,你可以使用二元印章找出插入索引。如果不知道Children的類型,但很難提供示例代碼,但已經有List<T>.BinarySearchArray.BinarySearch

學習LINQ是令人敬佩的 - 但它也是重要的是學會在使用LINQ是不是去:)

+0

指向BinarySeach的指針正是我所需要的。 – 2010-11-15 19:44:50

3

假設this.ChildrenList<T>,你可以使用List<T>.BinarySearch與自定義比較有效地找到最好的方式位置在插入新元素:

IComparer<Foo> comparer = AnonymousComparer.Create<Foo>(
    (x, y) => string.Compare(x.Name, y.Name)); 

int index = this.Children.BinarySearch(d, comparer); 
if (index < 0) index = ~index; 
this.Children.Insert(index, d); 

static class AnonymousComparer 
{ 
    public static IComparer<T> Create<T>(Func<T, T, int> comparer) 
    { 
     if (comparer == null) { throw new ArgumentNullException("comparer"); } 
     return new TheComparer<T>(comparer); 
    } 
    private class TheComparer<T> : IComparer<T> 
    { 
     private readonly Func<T, T, int> c; 
     public TheComparer(Func<T, T, int> c) { this.c = c; } 
     int IComparer<T>.Compare(T x, T y) { return this.c(x, y); } 
    } 
} 
+0

謝謝。我正在更新一個WPF的ObservableCollection,它似乎沒有實現二進制搜索並將其轉換爲數組,我認爲是太多了... – akonsu 2010-09-23 17:08:28

+0

再次感謝。我最終使用二進制搜索,正如所有回覆的人所建議的那樣。你的TheComparer 是非常有用的,我只用它不同(從列表的擴展方法內)。 – akonsu 2010-09-24 01:06:08

0

嗯,你可以送花兒給人只是使用排序依據添加新元素後,...

var v = this.Children.Union(new List<TypeOfChildren>() { d }).OrderBy<TypeOfChildren, string>(x => x.Name).ToList<TypeOfChildren>(); 
1

我創建了自己的擴展方法,以正確的順序添加新項:

public static class ListExtension 
{ 
    public static void InsertOrderedBy<TSource, TKey>(this IList<TSource> source, TSource item, Func<TSource, TKey> keySelector) where TKey : IComparable<TKey> 
    { 
     var i = source.Select((Value, Index) => new { Value, Index }).FirstOrDefault(x => keySelector(x.Value).CompareTo(keySelector(item)) > 0); 

     if (i == null) 
     { 
      source.Add(item); 
     } 
     else 
     { 
      source.Insert(i.Index, item); 
     } 
    } 
} 

我用這樣的:

List<Item> ItemList = new List<Item>(); 
ItemList.InsertOrderedBy(item, x => x.Duration); 

這和Jon Skeet的回答幾乎一樣,但我可以通過sort參數作爲第二個參數,例如持續時間(類型TimeSpan)。