2010-09-10 8 views
10

un método de extensión en una colección llamada MeasurementCollection comprueba si la propiedad Template.Frequency (Enum) de cada elemento tiene el mismo valor.Compruebe si todos los elementos de una Colección tienen el mismo valor

public static bool IsQuantized(this MeasurementCollection items) 
    { 
     return (from i in items 
       select i.Template.Frequency) 
       .Distinct() 
       .Count()==1; 

    } 

edición información sobre las clases subyacentes

MeasurementCollection : ICollection<IMeasurement> 

    IMeasurement 
    { 
    IMeasurementTemplate Template { get; }   
    ...... 
    } 

Es este un enfoque correcto o hay una solución más fácil que ya están en LINQ? Este método se usará intenso en la aplicación.

¿Tienes consejos para llevar conmigo, de vuelta al tablero de dibujo?

+1

Lo que es usted es requisito importante es la lectura? es la velocidad importante? ¿Cuál es el escenario habitual? ¿Que son lo mismo o que no son lo mismo? –

+1

Basado en la etiqueta #arrays que usted pone en esta pregunta, ¿podemos suponer que su objeto MeasurementCollection se almacena como una matriz o ArrayList? ¿Podemos obtener un '.Count' de una MeasurementCollection sin incurrir en demasiados gastos generales? La respuesta correcta dependerá de saber más sobre su colección subyacente. – StriplingWarrior

+0

¿Por qué no desea agregar este método (con cualquier implementación) a MeasurementCollection? ¿Por qué usas el método de extensión para esto? Si esta funcionalidad es parte de esta abstracción en particular muéstrela explícitamente. –

Respuesta

8

Editar: para hacer frente a las preocupaciones de Timwi aproximadamente 3 encuestadores:

bool same = <your default> ; 
var first = items.FirstOrDefault(); 
if (first != null) // assuming it's a class 
{ 
    same = items.Skip(1).All(i => i.Template.Frequency == first.Template.Frequency); 
} 

que todavía utiliza 2 encuestadores. No es un problema para el promedio List<>, pero para una consulta de base de datos que podría pagar para usar el menos legible:

bool same = <your default> ; 
Item first = null; 

foreach(var item in items) 
{ 
    if (first == null) 
    { 
     first = item; 
     same = true; 
    } 
    else 
    { 
     if (item.Template.Frequency != first.Template.Frequency) 
     { 
      same = false; 
      break; 
     } 
    } 
} 
+0

tu código no hace la misma comparación que OPs. Está comparando una propiedad de una propiedad. El suyo está haciendo una comparación de ref –

+1

@Rune: Añadiré eso para completarlo pero no es una diferencia fundamental. A menos que algunas propiedades sean nulas lo que la convertiría en un desastre. –

+0

Esto es lo mismo que el 'Trick # 2' en [mi segunda respuesta] (http://stackoverflow.com/questions/3687597/check-if-all-items-in-a-collection-have-the-same -valor/3687724 # 3687724). Instala dos enumeradores y evalúa el primer elemento dos veces, y si no desea lanzarlo, necesitaría un tercero para verificar 'Any()' primero. Esto puede ser un problema de rendimiento si la colección es floja y el primer elemento tarda un tiempo en computarse. – Timwi

2

que sería más rápido de esta manera:

public static bool IsQuantized(this MeasurementCollection items) 
{ 
    if(items == null || items.Count == 0) 
     return true; 

    var valueToCompare = items.First().Template.Frequency; 

    return items.All(i => i.Template.Frequency == valueToCompare); 
} 

volverá falsa en la primera la frecuencia de la plantilla del elemento es diferente, mientras que en su código, el algoritmo pasa la colección completa.

+0

Esto supone que la colección es indexable. – Timwi

+0

¡Allí lo arregló! –

+0

El OP puso una etiqueta #arrays en esta pregunta, lo que puede indicar que MeasurementCollection se implementó como una matriz o estructura similar. – StriplingWarrior

4

Primero de un consejo linq general. Si solo quieres saber si hay exactamente uno en una colección, usa Single() o SingleOrDefault(). Count posiblemente iterará toda la colección, que es más de lo que necesita, ya que puede rescatar si hay dos.

public static bool IsQuantized(this MeasurementCollection items) 
     { 
      var first = items.FirstOrDefault(); 
      return first != null && items.Skip(1).All(i => first.Template.Frequency == i.Template.Frequency)); 
     } 
+0

¿Por qué el voto a favor? –

+0

No es mi -1, pero parece que su código no trataría muy bien una colección vacía. Y tú eras el perfeccionista. –

+0

@Henk tienes razón, así que actualicé mi respuesta. Sin embargo, lo llamaría perfeccionista para comentar sobre una implementación que en la mayoría de los casos arrojaría una respuesta incorrecta. Tampoco veo cómo elogiar su enfoque general por su legibilidad es perfeccionista :) –

7

Se podía encontrar el primer valor y comprobar si alguno otros son diferentes, esto va a evitar tener que eval toda la colección (a menos que el valor único diferente es el último)

public static bool IsQuantized(this MeasurementCollection items) 
{ 
    if(!items.Any()) 
     return false; //or true depending on your use case 

    //might want to check that Template is not null, a bit a violation of level of demeter, but just an example 
    var firstProp = item.First().Template.Frequency; 

    return !items.Any(x=> x.Template.Frequency != firstProp); 

} 
3

I obtuve un poco de inspiración y pensé en una solución con solo la velocidad en mente. Esto realmente no es tan fácil de leer (que generalmente prefiero), pero las características cuando se trata de velocidad deberían ser bastante buenas.

El peor caso es el mismo para la mayoría de las otras implementaciones O (n), pero es muy poco probable, ya que requeriría toda la primera mitad de los elementos a ser igual y el segundo medio a ser todos iguales, pero no igual al valor en la primera mitad. y requeriría el mismo número de comparaciones que una búsqueda lineal. En la mayoría de los otros casos con el primero impar en un lugar aleatorio, esto requerirá la mitad de comparaciones que el lineal. En el caso donde los valores están en pares. Entonces ese artículo [0] == artículo [1] y artículo [2] == artículo [3] y artículo [0]! = Artículo [2] (y similar) entonces la búsqueda lineal será más rápida. En general, ya sea con datos aleatorios o unos pocos extraño una vez que esta debe ser más rápido que una búsqueda lineal

public static bool AllSame<T>(this IEnumerable<T> source, 
           IEqualityComparer<T> comparer = null) 
     { 
      if (source == null) 
       throw new ArgumentNullException("source cannot be null.", "source"); 

      if (comparer == null) 
       comparer = EqualityComparer<T>.Default; 
      var enumerator = source.GetEnumerator(); 

      return source.Zip(comparer); 
     } 

     private static bool Zip<T>(this IEnumerable<T> sequence, IEqualityComparer<T> comparer) 
     { 
      var result = new List<T>(); 
      var enumerator = sequence.GetEnumerator(); 
      while (enumerator.MoveNext()) 
      { 
       var first = enumerator.Current; 
       result.Add(enumerator.Current); 
       if (enumerator.MoveNext()) 
       { 
        if (!comparer.Equals(first, enumerator.Current)) 
        { 
         return false; 
        } 
       } 
       else 
       { 
        break; 
       } 
      } 
      return result.Count == 1 ? true : result.Zip(comparer); 
     } 

con fuera de optimización llamada de cola esta utiliza memoria adicional (con un peor caso de una cantidad de memoria cerca de la cantidad de memoria utilizada para la fuente original). La pila de llamadas no debería llegar a profundidad, ya que ninguna implementación concreta de IEnumerable (al menos que yo sepa) puede contener más que elementos int.MaxValue. lo que requeriría un máximo de 31 recursiones.

0

que he hecho de esta manera:

public static bool Same<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
    { 
     var val = source.Select(keySelector).FirstOrDefault(); 

     return source.Select(keySelector).All(a => Object.Equals(a, val)); 
    } 

Uso:

ddlStatus.AppendDataBoundItems = true; 
ddlStatus.Items.Add(new ListItem("<Mixed>", "-1")); 
ddlStatus.DataSource = ctx.Status.OrderBy(s => s.AssetStatus).ToList(); 
ddlStatus.DataTextField = "AssetStatus"; 
ddlStatus.DataValueField = "id"; 
ddlStatus.SelectedValue = Assets.Same(a => a.AssetStatusID) ? Assets.FirstOrDefault().AssetStatusID.ToString() : "-1"; 
ddlStatus.DataBind(); 

Este es un cuadro desplegable con una lista de estados disponibles. La forma edita múltiples activos. El menú desplegable necesita saber si todos los activos tienen el mismo valor o no. Mi misma extensión hace esto.

0

Yo sugeriría siguiente solución:

private static bool IsSameCollections(ICollection<> collection1, ICollection<> collection2) 
     { 
      return collection1.Count == collection2.Count && 
    (collection1.Intersect(collection2).Count() == collection1.Count); 
     } 
Cuestiones relacionadas