2012-05-24 3 views
2

Je n'arrive pas à trouver un moyen efficace mais simple de vérifier si une liste contient une autre liste (ordre de conservation). C'est analogue à la chaîne. Contient (fonctionnalité).La liste LINQ contient une autre liste (contiguë)

Dire que j'ai quatre collections de ints:

A = [1, 2, 3, 4, 5] 
B = [2, 3] 
C = [5, 6, 7] 
D = [3, 2, 4] 

A.Contains(B) serait vrai, alors que A.Contains(C) et A.Contains(D) serait faux.

Je préfère ne pas utiliser les itérateurs si cela peut être utile, mais je ne peux pas imaginer un moyen efficace de le faire; le code suivant est extrêmement inefficace.

public static bool IsSequentiallyEqual<T>(this IEnumerable<T> lhs, IEnumerable<T> rhs) 
{ 
     return lhs.Zip(rhs, (a, b) => a.Equals(b)).All(isEqual => isEqual == true); 
} 

public static bool StartsWith<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) 
{ 
     return haystack.Take(needle.Count()).IsSequentiallyEqual(needle); 
} 

public static bool Contains<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) 
{ 
     var result = list.SkipWhile((ele, index) => haystack.Skip(index).StartsWith(needle)); 
     return result.Count() >= needle.Count(); 
} 
+0

Combien d'articles aurez-vous? (C'est-à-dire, l'efficacité est-elle cruciale, ou voulez-vous juste quelque chose qui n'est pas très inefficace?) – Ryan

+0

Il ne suffira pas d'EXIGER l'efficacité, mais ce serait bien – hehewaffles

+2

http://stackoverflow.com/questions/3529727/comment-trouver-index-de-sous-liste-dans-liste – Ryan

Répondre

1
public static bool Contains<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) 
{ 
    var hayList = haystack.ToList(); 
    var needleList = needle.ToList(); 
    return Enumerable.Range(0, hayList.Count) 
        .Select(start => hayList.Skip(start).Take(needleList.Count)) 
        .Any(subsequence => subsequence.SequenceEqual(needleList)); 
} 
+0

Toujours O (N^2) mais j'aime beaucoup celle-ci – hehewaffles

2
public static bool Contains<T>(this IEnumerable<T> first, IEnumerable<T> second) 
{ 
     return string.Join("~", first).Contains(string.Join("~", second)); 
} 

Un peu moins « klugy », au moins éviter un travail pendant de longues listes longues.

public static bool Contains<T>(this IEnumerable<T> first, IEnumerable<T> second) 
    { 
     //trying to avoid multiple enumeration 
     var firstList = first.ToList(); 
     var secondList = second.ToList(); 

     if (!secondList.Any(firstList.Contains)) return false; 
     if (secondList.Count() > firstList.Count()) return false; 
     if (Math.Max(firstList.Count(), secondList.Count()) > 99999) 
      throw new ShouldNotUseThisUglyMethodException("I'm too kludgy to be used. Let me die..."); 
     return string.Join("~", firstList).Contains(string.Join("~", secondList)); 
    } 
+0

Cela semble terriblement klugy, mais cela fonctionne, merci – hehewaffles

+0

Si vous voulez moins kludgy que cela, utilisez '.ToArray()' sur vos listes puis utilisez un algorithme similaire à String.Contains();) –

-1

Utilisez le travail des hachages. Notez qu'il y a quelques contrôles qui pourraient être faits pour retourner immédiatement un faux, mais je montre seulement la viande du processus. Ici, il est en format d'extension pratique:

Mise à jour à poignée Référence

void Main() 
{ 
    var first  = new List<int>() { 1, 2, 5 }; 
    var firstInOrder = new List<int>() { 1, 2, 3 }; 
    var second  = new List<int>() { 1, 2, 3, 4, 5 }; 
    var third  = new List<int>() { 1, 10, 20 }; 

    Console.WriteLine(first.FoundInOther(second));  // False 
    Console.WriteLine(firstInOrder.FoundInOther(second)); // True 
    Console.WriteLine(first.FoundInOther(third));   // False 

} 

public static class NumberExtensions 
{ 

    public static bool FoundInOther(this IEnumerable<int> initial, IEnumerable<int> other) 
    { 
     int index = -1; 
     var asDictionary = other.ToDictionary(itm => itm, itm => ++index); 

     index = -1; 
     return initial.All(oth => asDictionary.ContainsKey(oth) && (asDictionary[oth] == ++index)); 
    } 

} 
+0

Essayez avec 'var fourth = new List () {5, 2}; 'Votre méthode retourne' true', quand je voudrais qu'elle retourne 'false' (l'ordre est important). – hehewaffles

+0

@hehewaffles Fait voir l'exemple. Il suffit de mettre dans l'index dans le conteneur du KVP. – OmegaMan

+0

Il ne fait que tester le début de la séquence. Par exemple, '{2, 3}' devrait être trouvé dans '{1, 2, 3, 4, 5}'. –

0

Cette version utilise une file d'attente pour stocker les séquences possibles. Il ne passe qu'une fois à haystack une fois mis à part le Take() initial, et il arrête d'itérer une fois qu'il trouve une correspondance. Cependant, il mute des variables dans une instruction LINQ.

public static bool Contains<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) 
{ 
    var needleList = needle.ToList(); 
    var queue = new Queue<T>(haystack.Take(needleList.Count - 1)); 
    return haystack.Skip(needleList.Count - 1) 
        .Any(hay => 
         { 
          queue.Enqueue(hay); 
          bool areEqual = queue.SequenceEqual(needleList); 
          queue.Dequeue(); 
          return areEqual; 
         }); 
} 
Questions connexes