Wie kann ich überprüfen, ob alle Listenelemente denselben Wert haben, und ihn zurückgeben oder einen "anderen Wert" zurückgeben, wenn dies nicht der Fall ist?

121

Wenn alle Elemente in einer Liste denselben Wert haben, muss ich diesen Wert verwenden, andernfalls muss ich einen „anderen Wert“ verwenden. Ich kann mir keinen einfachen und klaren Weg vorstellen, dies zu tun.

Siehe auch Ordentliche Methode zum Schreiben einer Schleife mit spezieller Logik für das erste Element in einer Sammlung.

Ian Ringrose
quelle
Auf Ihren ziemlich frechen Aufmerksamkeitsgewinner würde ich mit Anis Antwort stackoverflow.com/questions/4390232/…
Binary Worrier
5
Was möchten Sie tun, wenn es keinen ersten Wert gibt, weil die Liste leer ist? In diesem Fall ist es wahr, dass "alle Elemente in der Liste den gleichen Wert haben" - wenn Sie mir nicht glauben, finden Sie mir einen, der dies nicht tut! Sie definieren nicht, was in dieser Situation zu tun ist. Sollte dies eine Ausnahme auslösen, den "anderen" Wert zurückgeben, oder was?
Eric Lippert
@ Eric, sorry, wenn die Liste leer ist, sollte sie den "anderen" Wert zurückgeben
Ian Ringrose

Antworten:

152
var val = yyy.First().Value;
return yyy.All(x=>x.Value == val) ? val : otherValue; 

Die sauberste Art, die ich mir vorstellen kann. Sie können es zu einem Einzeiler machen, indem Sie val einfügen, aber First () wird n-mal ausgewertet, wodurch die Ausführungszeit verdoppelt wird.

Um das in den Kommentaren angegebene Verhalten "leerer Satz" zu berücksichtigen, fügen Sie einfach eine weitere Zeile vor den beiden oben genannten hinzu:

if(yyy == null || !yyy.Any()) return otherValue;
KeithS
quelle
1
+1, würde die Verwendung .Anyzulassen, dass die Aufzählung in Fällen mit unterschiedlichen Werten vorzeitig beendet wird?
Jeff Ogata
12
@adrift: Allwird beendet, sobald es ein Element xder Sequenz trifft , für das x.Value != val. In ähnlicher Weise Any(x => x.Value != val)würde enden, sobald es ein Element xder Sequenz trifft , für die x.Value != val. Das heißt, beide Allund Anyweisen einen "Kurzschluss" analog zu &&und auf ||(was effektiv was ist Allund Anyist).
Jason
@ Jason: genau. Alle (Bedingung) ist effektiv! Jede (! Bedingung), und die Bewertung von beiden wird beendet, sobald die Antwort bekannt ist.
KeithS
4
Mikrooptimierung:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
Caltor
100

Guter Schnelltest für alle gleich:

collection.Distinct().Count() == 1
Jeremy Bell
quelle
1
Dies funktioniert nicht mit jedem Class, obwohl es mit Strukturen funktionieren sollte. Ideal für eine Liste von Grundelementen.
Andrew Backer
2
+1 viel sauberer als die IMO-Lösung von KeithS. Möglicherweise möchten Sie verwenden, collection.Distinct().Count() <= 1 wenn Sie leere Sammlungen zulassen möchten.
3dGrabber
4
Seien Sie vorsichtig, .Distinct()funktioniert nicht immer wie erwartet - besonders wenn Sie mit Objekten arbeiten, lesen Sie diese Frage. In diesem Fall müssen Sie die IEquatable-Schnittstelle implementieren.
Matt
16
Sauberer, ja, aber im Durchschnitt weniger performant; Distinct () durchläuft garantiert jedes einzelne Element in der Sammlung einmal, und im schlimmsten Fall, wenn jedes Element anders ist, durchläuft Count () die vollständige Liste zweimal. Distinct () erstellt auch ein HashSet, sodass sein Verhalten linear und nicht NlogN oder schlechter sein kann, was die Speichernutzung erhöht. All () führt im schlimmsten Fall einen vollständigen Durchgang durch, wenn alle Elemente gleich sind, und erstellt keine neuen Sammlungen.
KeithS
1
@KeithS Wie Sie hoffentlich inzwischen feststellen, Distinctwird die Sammlung überhaupt nicht durchlaufen, und es Countwird eine Durchquerung über Distinctden Iterator durchgeführt.
NetMage
22

Obwohl Sie ein solches Gerät sicherlich aus vorhandenen Sequenzoperatoren erstellen können, wäre ich in diesem Fall geneigt, dieses als benutzerdefinierten Sequenzoperator zu schreiben. Etwas wie:

// Returns "other" if the list is empty.
// Returns "other" if the list is non-empty and there are two different elements.
// Returns the element of the list if it is non-empty and all elements are the same.
public static int Unanimous(this IEnumerable<int> sequence, int other)
{
    int? first = null;
    foreach(var item in sequence)
    {
        if (first == null)
            first = item;
        else if (first.Value != item)
            return other;
    }
    return first ?? other;
}

Das ist ziemlich klar, kurz, deckt alle Fälle ab und erzeugt nicht unnötig zusätzliche Iterationen der Sequenz.

Dies zu einer generischen Methode zu machen, die funktioniert, IEnumerable<T>bleibt als Übung übrig. :-)

Eric Lippert
quelle
Angenommen, Sie haben eine Folge von Nullen und der extrahierte Wert ist auch eine Null. In diesem Fall könnte die Sequenz leer sein oder jedes Element in der Sequenz könnte eine Null im extrahierten Wert haben. Das Zusammenwachsen würde in diesem Fall das zurückgeben, otherwenn das nulltatsächlich die (vermutlich) richtige Antwort war. Angenommen, die Funktion war T Unanimous<U, T>(this IEnumerable<U> sequence, T other)oder eine solche Signatur, die es ein wenig kompliziert.
Anthony Pegram
@Anthony: In der Tat gibt es hier viele Komplikationen, aber sie lassen sich ziemlich leicht umgehen. Ich verwende ein nullable int als Annehmlichkeit, damit ich nicht das Flag "Ich habe das erste Element bereits gesehen" deklarieren muss. Sie können die Flagge einfach deklarieren. Außerdem verwende ich "int" anstelle von T, weil ich weiß, dass Sie immer zwei Ints auf Gleichheit vergleichen können, was bei zwei Ts nicht der Fall ist. Dies ist eher eine Skizze einer Lösung als eine voll funktionsfähige generische Lösung.
Eric Lippert
13
return collection.All(i => i == collection.First())) 
    ? collection.First() : otherValue;.

Oder wenn Sie sich Sorgen machen, First () für jedes Element auszuführen (was ein gültiges Leistungsproblem sein könnte):

var first = collection.First();
return collection.All(i => i == first) ? first : otherValue;
Justin Niessner
quelle
@KeithS - Deshalb habe ich den zweiten Teil meiner Antwort hinzugefügt. Bei kleinen Sammlungen ist das Aufrufen von First () trivial. Bei großen Sammlungen kann dies zu einem Problem werden.
Justin Niessner
1
"Bei kleinen Sammlungen ist es trivial, First () aufzurufen." - Das hängt von der Quelle der Sammlung ab. Für eine Liste oder ein Array einfacher Objekte haben Sie absolut Recht. Einige Aufzählungen sind jedoch keine endlichen speichergespeicherten Mengen von Grundelementen. Eine Sammlung von Delegierten oder ein Enumerator, der durch eine algorithmische Reihenberechnung (z. B. Fibonacci) ergibt, würde bei jeder Auswertung von First () sehr teuer werden.
KeithS
5
Oder schlimmer, wenn es sich bei der Abfrage um eine Datenbankabfrage handelt und der Aufruf von "First" jedes Mal erneut auf die Datenbank trifft.
Eric Lippert
1
Es wird schlimmer, wenn Sie eine einmalige Iteration wie das Lesen aus einer Datei haben ... Also sieht Anis Antwort aus einem anderen Thread am besten aus.
Alexei Levenkov
@ Eric - Komm schon. Es ist nichts Falsches daran, die Datenbank dreimal für jedes Element zu treffen ... :-P
Justin Niessner
3

Dies mag spät sein, aber eine Erweiterung, die für Wert- und Referenztypen gleichermaßen funktioniert, basierend auf Erics Antwort:

public static partial class Extensions
{
    public static Nullable<T> Unanimous<T>(this IEnumerable<Nullable<T>> sequence, Nullable<T> other, IEqualityComparer comparer = null)  where T : struct, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (Nullable<T>)first ?? other;
    }

    public static T Unanimous<T>(this IEnumerable<T> sequence, T other, IEqualityComparer comparer = null)  where T : class, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (T)first ?? other;
    }
}
Batta
quelle
1
public int GetResult(List<int> list){
int first = list.First();
return list.All(x => x == first) ? first : SOME_OTHER_VALUE;
}
Hackerhasid
quelle
1

Eine Alternative zur Verwendung von LINQ:

var set = new HashSet<int>(values);
return (1 == set.Count) ? values.First() : otherValue;

Ich habe festgestellt, dass die Verwendung HashSet<T>für Listen mit bis zu ~ 6.000 Ganzzahlen schneller ist als:

var value1 = items.First();
return values.All(v => v == value1) ? value1: otherValue;
Ɖiamond ǤeezeƦ
quelle
Erstens kann dies viel Müll verursachen. Es ist auch weniger klar als die anderen LINQ-Antworten, aber langsamer als die Antwort der Erweiterungsmethode.
Ian Ringrose
Wahr. Es wird jedoch nicht viel Müll sein, wenn wir darüber sprechen, ob ein kleiner Satz von Werten alle gleich ist. Als ich dies und eine LINQ-Anweisung in LINQPad für einen kleinen Satz von Werten ausführte, war HashSet schneller (zeitgesteuert mit der Stopwatch-Klasse).
Ǥiamond ǤeezeƦ
Wenn Sie es in einem Release-Build über die Befehlszeile ausführen, erhalten Sie möglicherweise unterschiedliche Ergebnisse.
Ian Ringrose
Erstellt eine Konsolenanwendung und stellt fest, dass dies HashSet<T>anfangs schneller ist als die Verwendung der LINQ-Anweisungen in meiner Antwort. Wenn ich dies jedoch in einer Schleife mache, ist LINQ schneller.
Ǥiamond ǤeezeƦ
Das große Problem bei dieser Lösung ist, dass Sie, wenn Sie Ihre benutzerdefinierten Klassen verwenden, Ihre eigenen implementieren müssen GetHashCode(), was schwierig korrekt durchzuführen ist. Weitere Informationen finden Sie unter: stackoverflow.com/a/371348/2607840 .
Cameron
0

Eine geringfügige Abweichung vom obigen vereinfachten Ansatz.

var result = yyy.Distinct().Count() == yyy.Count();

David Ehnis
quelle
3
Dies ist genau umgekehrt. Dadurch wird überprüft, ob jedes Element in der Liste eindeutig ist.
Mario Galea
-1

Wenn ein Array vom Typ Multidimension wie unten ist, müssen wir unter linq schreiben, um die Daten zu überprüfen.

Beispiel: Hier sind die Elemente 0 und ich überprüfe, ob alle Werte 0 sind oder nicht.
ip1 =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0 0 0

    var value=ip1[0][0];  //got the first index value
    var equalValue = ip1.Any(x=>x.Any(xy=>xy.Equals()));  //check with all elements value 
    if(equalValue)//returns true or false  
    {  
    return "Same Numbers";  
    }else{  
    return "Different Numbers";   
    }
Pradeep Kumar Das
quelle