8
votes

Comment obtenir une liste aplatie de la liste de classe imbriquée ?

J'ai un Question en utilisant ces mêmes exemples - Cette question est axée sur un problème différent. Compte tenu des classes suivantes: xxx

si famille a une méthode définie comme telle: xxx

J'ai besoin de être capable d'exécuter le prédicat sur une liste aplatie de la personne . Dans l'exemple ci-dessus SelectMany n'atteint pas la liste comme je l'avais espéré. Ce qui précède ne sera pas compilé car le type inféré ne peut pas être déterminé.

Comment puis-je obtenir la collection family.Person pour devenir une liste aplatie de personne?


1 commentaires

Si vous avez des boucles dans votre structure de données, vous pouvez cette solution: Stackoverflow.com/Questions/141467/Recursive-list-Flatting / ...


5 Réponses :


2
votes

family.person code> est déjà em> une liste aplatie; Il n'est pas nécessaire d'appeler SelectMany code> dessus.

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


2 commentaires

Notez que la personne a une liste imbriquée . Il est pas une liste aplatie, c'est une structure de données récursif.


@Matt - Je n'ai totalement pas vu ça - surtout en considérant le code d'origine de l'OP de family.person.selectmany (p => p) je modifierais pour réparer, mais cela ressemble à des peines déjà frappés hors du parc



3
votes

Vous n'avez pas besoin de SelectMany code> et Rendement de rendement code> - Vous avez besoin de l'une ou de l'autre:

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


3 commentaires

'p => prédicat (p)' peut être réduit à juste "prédicat" - guichet automatique Vous enveloppez une Func avec un autre.


@Mattdavavey J'ai ajouté une enveloppe pour enlever la "magie" de l'expression. J'ai ajouté une ligne de commentaire pour dire que cela peut être simplifié. C'est juste que la recherche d'un clause sans => est peut-être déroutant aux personnes ayant une expérience relativement peu linq.


ouais je pense que c'est une chose pertinente à faire :)



6
votes

À ma connaissance, le moyen le plus simple d'accomplir est d'utiliser une aide.

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


1 commentaires

Merci! et merci! PS J'utilise ce modèle assez souvent que j'ai écrit une version générique en tant que méthode d'extension. Super utile.



4
votes

SelectMany n'aplatit que un niveau de hiérarchie:

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


0 commentaires

5
votes
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);
}

2 commentaires

+1 Pour avoir repéré la structure de données récursive, la question ne faisait pas assez évident ce point :)


Je recommande Concat sur un syndicat car il n'est pas nécessaire de faire passer des doublons croisés.