2012-01-12 9 views
7

私はquestion using these same examplesを持っています - この質問は別の問題に焦点を当てています。 Familyのような定義されたメソッドがある場合はネストされたクラスリストからフラットなリストを取得する方法<T>?

[XmlRoot] 
    public class Family { 

     [XmlElement] 
     public List<Person> Person; 
    } 

    public class Person { 

     [XmlAttribute("member")] 
     public MemberType Member { get; set; } 

     [XmlAttribute("id")] 
     public int Id { get; set; } 

     [XmlElement] 
     public string Surname { get; set; } 

     [XmlElement] 
     public string Forename { get; set; } 

     [XmlElement("Person")] 
     public List<Person> People; 
    } 

    public enum MemberType { 
     Father, 
     Mother, 
     Son, 
     Daughter 
    } 

: は、以下のクラスを考える

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    // how do I get SelectMany to flatten the list? 
    foreach (var p in family.Person.SelectMany(p => p)) { 
     if(predicate(p)) { 
      yield return p; 
     } 
    } 
} 

を私はPersonの平坦化されたリスト上の述語を実行できるようにする必要があります。上記の例では、SelectManyは私が望むようにリストを平坦化していません。推論された型を決定することができないため、上記は実際にはコンパイルされません。

Family.PersonコレクションをPersonの1つのフラットなリストにするにはどうすればよいですか?

+0

あなたは 'P => p.People'を試してみましたか? – jvstech

+0

データ構造にループがある場合は、このソリューションを使用できます。http://stackoverflow.com/questions/141467/recursive-list-flattening/24747394#answer-24747394 – Aidin

答えて

5
public IEnumerable<Person> Find(IEnumerable<Person> input, Func<Person, bool> predicate) { 
    return input.Select(p => 
     { 
      var thisLevel = new List<Person>(); 
      if(predicate(p)) 
       thisLevel.Add(p); 

      return thisLevel.Union(Find(p.People ?? new List<Person>(), predicate)); 
     } 
    ).SelectMany(p => p); 
} 
+0

+1再帰的なデータ構造を見つけるための質問は、この点を十分に明白にしていませんでした:) – MattDavey

+2

私は交差集合の重複をスクリーニングする必要がないので、連合上の連合をお勧めします。 –

2

family.Personはすでにです。それにSelectManyを呼び出す必要はありません。

foreach (var p in family.Person) { 
    if(predicate(p)) { 
     yield return p; 
    } 
} 

はまた、あなたがより簡単に行うことができます。

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    return family.Person.Where(predicate); 
} 
+0

Personにネストされた*リスト *があることに注意してください。これは**平易なリストではなく、再帰的なデータ構造です。 – MattDavey

+1

@Matt - 私は完全にそれを見ていませんでした。特に、OPのオリジナルコード、family.Person.SelectMany(p => p)を考慮して修正しましたが、Petrはすでに公園からそれをノックアウトしたようです –

3

あなたがSelectManyyield returnの両方を必要としない - あなたは、どちらか一方が必要です

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    foreach (var p in family.Person) { 
     if(predicate(p)) { 
      yield return p; 
     } 
    } 
} 

OR

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    return family.Person.Where(p => predicate(p)); // Can be written simply as Where(predicate) 
} 
+1

* 'p =>述語(p)' *を* '述語' * - atmに減らすことができます。* Func *を別のものにラップしています。 – MattDavey

+1

@MattDavey式から「魔法」を取り除くためのラッパーを追加しました。私はそれが簡素化できると言うためにコメント行を追加しました。 LINQの経験が比較的少ない人に混乱を招く可能性のある '=>'を除いた 'Where'節を見るだけです。 – dasblinkenlight

+0

ええ私はそれが適切なことだと思う:) – MattDavey

6

私の知る限り、これを達成する最も簡単な方法はヘルパーを使用することです。

private List<Person> FlattenTree(Person person) 
    { 
     var accumulator = new List<Person>(); 
     FlattenPersonHelper(person, accumulator); 

     return accumulator; 
    } 


    private void FlattenPersonHelper(Person person, List<Person> accumulator) 
    { 
     accumulator.Add(person); 

     foreach (var child in person.People) 
     { 
      FlattenPersonHelper(child, accumulator); 
     } 
     return; 
    } 

あなたは、このリストに対してあなたの述語を実行することができます:あなたは、実際に任意の深さの散歩をお勧めします

public IEnumerable<Person> FindLevel2 (Func<Person, bool> predicate) 
{ 
    return family.Person.SelectMany(p => p.People).Where(predicate); 
} 

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    var familyRoot = new Person() { People = family.Person }; 
    return FlattenTree(familyRoot).Where(predicate); 
} 
+2

+1私はそれも好きです - また、stackoverflowへようこそ! –

+1

ありがとう!ありがとう! PS私はこのパターンを頻繁に使用して、拡張メソッドとして汎用バージョンを作成しました。スーパー便利です。 –

4

SelectManyは、階層の1つのレベルを平坦化階層のそれは再帰(Untested)によって最も効果的です。

public IEnumerable<Person> Find(Func<Person, bool> predicate) 
{ 
    foreach(Person p in family.Person) 
    { 
    IEnumerable<Person> result = FindFromPerson(p); 
    foreach(Person x in result) 
    { 
     yield return x; 
    } 
    } 
} 

public IEnumerable<Person> FindFromPerson(Person p, Func<Person, bool> predicate) 
{ 
    if predicate(p) 
    { 
    yield return p; 
    } 
    foreach(Person child in p.People) 
    { 
    IEnumerable<Person> childResults = FindFromPerson(child); 
    foreach(Person x in childResults) 
    { 
     yield return x; 
    } 
    } 
} 
関連する問題