Come verificare se tutti gli elementi dell'elenco hanno lo stesso valore e restituirlo o restituire un "altroValore" in caso contrario?


122

Se tutti gli elementi di un elenco hanno lo stesso valore, allora devo usare quel valore, altrimenti devo usare un "altroValore". Non riesco a pensare a un modo semplice e chiaro per farlo.

Vedi anche Modo pulito per scrivere un ciclo che ha una logica speciale per il primo elemento in una raccolta.


Sulla tua attenzione piuttosto sfacciata, andrei con la risposta di Ani stackoverflow.com/questions/4390232/…
Binary Worrier

5
Cosa vuoi che accada se non c'è il primo valore perché l'elenco è vuoto? In quel caso è vero che "tutti gli elementi della lista hanno lo stesso valore" - se non mi credi, trovami uno che non lo sia! Non definisci cosa fare in questa situazione. Questo dovrebbe generare un'eccezione, restituire il valore "altro" o cosa?
Eric Lippert

@Eric, scusa quando l'elenco è vuoto dovrebbe restituire il valore "altro"
Ian Ringrose

Risposte:


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

Il modo più pulito a cui riesco a pensare. Puoi renderlo un one-liner inserendo val, ma First () verrebbe valutato n volte, raddoppiando il tempo di esecuzione.

Per incorporare il comportamento "set vuoto" specificato nei commenti, aggiungi semplicemente un'altra riga prima delle due precedenti:

if(yyy == null || !yyy.Any()) return otherValue;

1
+1, l'utilizzo .Anyconsentirebbe di chiudere anticipatamente l'enumerazione nei casi in cui sono presenti valori diversi?
Jeff Ogata

12
@adrift: Allterminerà non appena colpisce un elemento xdella sequenza per cui x.Value != val. Allo stesso modo, Any(x => x.Value != val)terminerà non appena colpisce un elemento xdella sequenza per cui x.Value != val. Cioè, entrambi Alle Anymostrano un "cortocircuito" analogo a &&e ||(che è effettivamente ciò che Alle Anysono).
jason

@ Jason: esattamente. All (condition) è effettivamente! Any (! Condition) e la valutazione di entrambe terminerà non appena la risposta sarà nota.
KeithS

4
Microottimizzazione:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
Caltor

101

Buon test rapido per tutti uguali:

collection.Distinct().Count() == 1

1
Non funzionerà con nessuno Class, anche se dovrebbe funzionare con gli struct. Ottimo per un elenco di primitivi, però.
Andrew Backer

2
+1 molto più pulito della soluzione di KeithS IMO. Potresti volerlo usare collection.Distinct().Count() <= 1 se vuoi consentire raccolte vuote.
3dGrabber

4
Fai attenzione, .Distinct()non sempre funziona come previsto, specialmente quando lavori con oggetti, vedi questa domanda. In questi casi, è necessario implementare l'interfaccia IEquatable.
Matt

16
Più pulito, sì, ma meno performante nel caso medio; È garantito che Distinct () attraversi ogni singolo elemento della raccolta una volta e, nel caso peggiore, ogni elemento è diverso, Count () attraverserà l'elenco completo due volte. Distinct () crea anche un HashSet in modo che il suo comportamento possa essere lineare e non NlogN o peggio, e questo aumenterà l'utilizzo della memoria. All () esegue un passaggio completo nel caso peggiore in cui tutti gli elementi siano uguali e non crea nuove raccolte.
KeithS

1
@KeithS Come spero tu capisca ormai, Distinctnon attraverserà affatto la raccolta e Countfarà un attraversamento tramite Distinctl'iteratore di.
NetMage

22

Sebbene tu possa certamente costruire un tale dispositivo con operatori di sequenza esistenti, in questo caso sarei propenso a scrivere questo come operatore di sequenza personalizzato. Qualcosa di simile a:

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

È abbastanza chiaro, breve, copre tutti i casi e non crea inutilmente iterazioni extra della sequenza.

Trasformare questo in un metodo generico che IEnumerable<T>funzioni è lasciato come esercizio. :-)


Supponiamo, ad esempio, di avere una sequenza di nullable e anche il valore estratto è un nullable. In tal caso, la sequenza potrebbe essere vuota o ogni elemento nella sequenza potrebbe avere un valore nullo nel valore estratto. La coalescenza, in questo caso, restituirebbe il othermomento in cui nullera effettivamente la (presumibilmente) risposta corretta. Diciamo che la funzione era T Unanimous<U, T>(this IEnumerable<U> sequence, T other)o qualche firma simile, questo complica un po '.
Anthony Pegram

@ Anthony: In effetti, ci sono molte complicazioni qui, ma sono abbastanza facilmente risolvibili. Sto usando un int nullable per comodità in modo da non dover dichiarare un flag "Ho già visto il primo elemento". Potresti facilmente dichiarare la bandiera. Inoltre sto usando "int" invece di T perché so che puoi sempre confrontare due int per uguaglianza, il che non è il caso di due Ts. Questo è più uno schizzo di una soluzione che una soluzione generica completamente funzionale.
Eric Lippert

13
return collection.All(i => i == collection.First())) 
    ? collection.First() : otherValue;.

O se sei preoccupato di eseguire First () per ogni elemento (che potrebbe essere un valido problema di prestazioni):

var first = collection.First();
return collection.All(i => i == first) ? first : otherValue;

@ KeithS - Ecco perché ho aggiunto la seconda parte della mia risposta. Su piccole raccolte, chiamare First () è banale. Su grandi collezioni, questo potrebbe iniziare a essere un problema.
Justin Niessner

1
"Su piccole raccolte, chiamare First () è banale." - Dipende dalla fonte della raccolta. Per un elenco o una serie di oggetti semplici, hai assolutamente ragione. Tuttavia, alcuni enumerabili non sono insiemi finiti di primitive memorizzati nella cache. Una raccolta di delegati, o un enumeratore che restituisce un calcolo di serie algoritmiche (ad esempio Fibonacci), sarebbe molto costoso valutare First () ogni volta.
KeithS

5
O peggio, se la query è una query di database e la chiamata "First" colpisce nuovamente il database ogni volta.
Eric Lippert

1
Peggio ancora quando hai iterazione una tantum come la lettura da file ... Quindi la risposta di Ani da un altro thread sembra la migliore.
Alexei Levenkov

@Eric - Andiamo. Non c'è niente di sbagliato nel colpire il database tre volte per ogni elemento ... :-P
Justin Niessner

3

Potrebbe essere in ritardo, ma un'estensione che funziona sia per i tipi di valore che per i tipi di riferimento in base alla risposta di Eric:

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

1
public int GetResult(List<int> list){
int first = list.First();
return list.All(x => x == first) ? first : SOME_OTHER_VALUE;
}

1

Un'alternativa all'uso di LINQ:

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

Ho scoperto che l'utilizzo HashSet<T>è più veloce per elenchi fino a ~ 6.000 numeri interi rispetto a:

var value1 = items.First();
return values.All(v => v == value1) ? value1: otherValue;

In primo luogo, questo può creare molta spazzatura. Inoltre è meno chiaro delle altre risposte LINQ, ma più lento delle risposte del metodo di estensione.
Ian Ringrose

Vero. Tuttavia, non ci sarà molta spazzatura se stiamo parlando di determinare se un piccolo insieme di valori sono tutti uguali. Quando ho eseguito questo e un'istruzione LINQ in LINQPad per un piccolo set di valori, HashSet è stato più veloce (temporizzato utilizzando la classe Stopwatch).
Ɖiamond ǤeezeƦ

Se lo esegui in una build di rilascio dalla riga di comando, potresti ottenere risultati diversi.
Ian Ringrose

HashSet<T>Ho creato un'applicazione console e ho scoperto che inizialmente è più veloce rispetto all'utilizzo delle istruzioni LINQ nella mia risposta. Tuttavia, se lo faccio in un ciclo, LINQ è più veloce.
Ɖiamond ǤeezeƦ

Il grosso problema con questa soluzione è che se stai usando le tue classi personalizzate, devi implementarne una tua GetHashCode(), cosa difficile da fare correttamente Vedi: stackoverflow.com/a/371348/2607840 per maggiori dettagli.
Cameron

0

Una leggera variazione rispetto all'approccio semplificato di cui sopra.

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


3
Questo è esattamente il contrario. Questo controllerà che ogni elemento nell'elenco sia Unico.
Mario Galea

-1

Se un array è di tipo multidimensionale come di seguito, dobbiamo scrivere sotto linq per controllare i dati.

esempio: qui gli elementi sono 0 e sto controllando che tutti i valori siano 0 o meno.
ip1 =
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";   
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.