2011-07-30 7 views
3

これはコメントと共にプログラムです。私はListAとListBを持っています。 ListAのIsChecked = trueをListBに設定していればそれを有効にしたいLINQクエリでリスト内の少数のアイテムをチェックしたままにします

foreach (var item in ListB) 
{ 
    var listAItem = (from itemA in ListA 
        where itemA.Title == item.Title 
        select itemA).First(); //no need for FirstOrDefault() because it is always going to be present 

    listAItem.IsChecked = true; 
} 

しかし、これは非常に非効率的になります。

public class SomeClass 
{ 
    public bool IsChecked { get; set; } 
    public string Title { get; set; } 
} 

List<SomeClass> ListA = new List<SomeClass> 
{ 
    new SomeClass { IsChecked = false, Title = "A" }, 
    new SomeClass { IsChecked = false, Title = "B" }, 
    new SomeClass { IsChecked = false, Title = "C" }, 
    new SomeClass { IsChecked = false, Title = "D" }, 
    new SomeClass { IsChecked = false, Title = "E" }, 
    new SomeClass { IsChecked = false, Title = "F" }, 
}; 

List<SomeClass> ListB = new List<SomeClass> 
{ 
    new SomeClass { Title = "A" }, 
    new SomeClass { Title = "D" }, 
    new SomeClass { Title = "F" }, 
}; 

// some linq query at the end of which listA's Item containing Title = "A","D","F" will become true 

foreach (var item in ListA) 
{ 
    Console.WriteLine(item.Title + " " + item.IsChecked); 
} 
Console.ReadKey(); 

これは私が思い付いたものです:これは、プログラムです。 Linqを使ってこれを行う良い方法はありますか?私はLINQのソリューションだけを望んでいます。事前に

感謝:)

答えて

5

あなたはこのようなJoinを使用することができます。

var itemAsToBeChecked = from itemA in ListA 
         join itemB in ListB on itemA.Title equals itemB.Title 
         select itemA; 

foreach (var itemA in itemAsToBeChecked) 
    itemA.IsChecked = true; 

私は各itemAのマッチング、ListAを列挙する前にJoinのMicrosoftの実装は、最初のListBSomeClassTitleからのルックアップ(Lookup<,>)を作成します信じるそれが行くにつれてルックアップ。これは現在のソリューションよりもはるかに効率的でなければなりません。

また、あなたは好むならHashSet<T>の使用を検討することができます

:もちろん

var titlesToBeChecked = new HashSet<string>(ListB.Select(itemB => itemB.Title)); 
var itemAsToBeChecked = ListA.Where(itemA => titlesToBeChecked.Contains(itemA.Title)); 

foreach (var itemA in itemAsToBeChecked) 
    itemA.IsChecked = true; 

を、それがTitleベースの平等の定義がある場合は特に、他の人が言及しているとして、あなたのタイプにIEqualityComparer<T>を実装するためにここに意味をなさないかもしれませんあなたのプログラムでよく使用されます。Equalsを実装した後

public class SomeClass : IEquatable<SomeClass> 
{ 
    public bool IsChecked { get; set; } 
    public string Title { get; set; } 

    public bool Equals(SomeClass other) 
    { 
     //Check whether the compared object is null. 
     if (ReferenceEquals(other, null)) return false; 

     //Check whether the compared object references the same data. 
     if (ReferenceEquals(this, other)) return true; 

     //Check whether SomeClass's properties are equal. 
     return Title.Equals(other.Title); 
    } 

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects. 
    public override int GetHashCode() 
    { 
     //Get hash code for the Title field if it is not null. 
     int hashSomeClassTitle = Title == null ? 0 : Title.GetHashCode(); 

     //Calculate the hash code for SomeClass. 
     return hashSomeClassTitle; 
    } 
} 

GetHashCodeあなたは行ってもいいです:

+0

+1。私はあなたの解決策が好きだった。 BrokenGlassが提供しているIntersectソリューションはどうでしょうか?それも参照を作成しますか?何か案が? – TCM

+0

@Anthony:あなたの型が適切な等価実装を持っていた場合、または適切な 'IEqualityComparer 'を提供した場合、 'Intersect'はうまく動作します。しかし、読者にとっては少し混乱するので、ここではIntersectが本当に好きではありません。いずれかのリストの一致する項目がすべての可能な意味で「等しい」場合は、通常は「交差」を使用します。しかし、あなたは結果がどのリストから来るべきかを非常に気にしています...(その方法に関する文書はあいまいではありませんが、それにもかかわらず不思議です)。 – Ani

2

あなたは

foreach(var item in ListA.Where(x => ListB.Contains(x))) 
    item.IsChecked = true; 

を行うことができます。これは、Titleプロパティを比較するIEquatable<SomeClass>を実装するクラスSomeClassが必要になります。

public class SomeClass : IEquatable<SomeClass> 
{ 
    public bool IsChecked { get; set; } 
    public string Title { get; set; } 

    public bool Equals(SomeClass other) 
    { 
     return this.Title == other.Title; 
    } 
} 

別の方法としては、Intersect()を使用することができますIEqualityComparer

012を提供する必要があります
foreach (var item in ListA.Intersect(ListB, new SomeClassComparer())) 
    item.IsChecked = true; 

... 
public class SomeClassComparer : IEqualityComparer<SomeClass> 
{ 
    public bool Equals(SomeClass x, SomeClass y) 
    { 
     if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) 
      return false; 
     return x.Title == y.Title; 
    } 

    public int GetHashCode(SomeClass obj) 
    { 
     return obj.Title.GetHashCode(); 
    } 
} 
1

一つの解決策(IComparableまたはIEquatableの実装を必要としない、場合にあなたがそのクラスを制御することはできません):

foreach (var item in ListA.Where(a => ListB.Any(b => b.Title == a.Title))) 
{ 
    item.IsChecked = true; 
} 

希望しない限り、あなたはこのforeachのが必要になりますことを心に留めておいてくださいListAを書き換えます。 Linqはクエリ言語であり、既存のリストを変更するために使用するものではありません。純粋なLinqを使用するにはここであなたの意図を隠す。 .Joinまたは.Zipを適切に使用して新しいListAを返す方法がありますが、従来の構文を使用して変更する方が常に良い方法です。

1
var q = from i1 in listA 
     from i2 in listB 
     where i1.Title == it2.Title 
     select i1; 

foreach (var i in q) 
{ 
    i.IsChecked = true; 
} 
1

私はEnumerable.Intersect演算子を使用

var intersection = ListA.Intersect(ListB); 

foreach (var item in intersection) 
{ 
    item.IsChecked = true; 

    Console.WriteLine(item.Title + " " + item.IsChecked); 
} 
関連する問題