2009-07-24 5 views
10

dicen que hay dos matrices:LINQ "zip" en la cadena de su matriz

String[] title = { "One","Two","three","Four"}; 
String[] user = { "rob","","john",""}; 

necesito para filtrar la matriz anterior, cuando el valor es user vacío y luego unirse a la cremallera o los dos juntos. Salida final debería ser como la siguiente:

{ "One:rob", "three:john" } 

¿Cómo se puede hacer esto utilizando LINQ?

+3

'Zip' es ahora un método estándar de .NET 4.0. http://msdn.microsoft.com/en-us/library/dd267698.aspx – Mashmagar

Respuesta

9

Parece que realmente desea "comprimir" los datos juntos (no unirse), es decir, emparejar en pares; ¿Es eso correcto? Si es así, simplemente:

var qry = from row in title.Zip(user, (t, u) => new { Title = t, User = u }) 
       where !string.IsNullOrEmpty(row.User) 
       select row.Title + ":" + row.User; 
    foreach (string s in qry) Console.WriteLine(s); 

mediante la operación de Ziphere:

// http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx 
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult> 
(this IEnumerable<TFirst> first, 
IEnumerable<TSecond> second, 
Func<TFirst, TSecond, TResult> resultSelector) 
{ 
    if (first == null) throw new ArgumentNullException("first"); 
    if (second == null) throw new ArgumentNullException("second"); 
    if (resultSelector == null) throw new ArgumentNullException("resultSelector"); 
    return ZipIterator(first, second, resultSelector); 
} 

private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult> 
    (IEnumerable<TFirst> first, 
    IEnumerable<TSecond> second, 
    Func<TFirst, TSecond, TResult> resultSelector) 
{ 
    using (IEnumerator<TFirst> e1 = first.GetEnumerator()) 
    using (IEnumerator<TSecond> e2 = second.GetEnumerator()) 
     while (e1.MoveNext() && e2.MoveNext()) 
      yield return resultSelector(e1.Current, e2.Current); 
} 
+0

Vaya, no he formado bien el ejemplo, los valores de la matriz no coincidirán en absoluto. Por favor remita la pregunta actualizada. – Kusek

+0

Me di cuenta de eso; reemplazado por "zip" –

+0

LOL - Me encanta el hecho de que ambos usamos el código de Eric. –

9

Para empezar, se necesita un operador Zip para comprimir las dos matrices conjuntamente. Aquí hay una versión abreviada del código de Eric Lippert's blog (comprobación de ningún error en esta versión, simplemente por razones de brevedad):

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult> 
    (this IEnumerable<TFirst> first, 
    IEnumerable<TSecond> second, 
    Func<TFirst, TSecond, TResult> resultSelector) 
{ 
    using (IEnumerator<TFirst> e1 = first.GetEnumerator()) 
     using (IEnumerator<TSecond> e2 = second.GetEnumerator()) 
      while (e1.MoveNext() && e2.MoveNext()) 
       yield return resultSelector(e1.Current, e2.Current); 
} 

Tenga en cuenta que Zip estará en las librerías estándar de .NET 4.0.

Luego solo tiene que aplicar un filtro y una proyección. Entonces obtendríamos:

var results = title.Zip(user, (Title, User) => new { Title, User }) 
        .Where(x => x.Title != "") 
        .Select(x => x.Title + ":" + x.User); 
5

Como complemento a las respuestas ya publicadas, aquí hay una solución sin usar el método Zip. Esto supone que ambas matrices tienen la misma longitud.

 var pairs = from idx in Enumerable.Range(0, title.Length) 
        let pair = new {Title = title[idx], User = user[idx]} 
        where !String.IsNullOrEmpty(pair.User) 
        select String.Format("{0}:{1}", pair.Title, pair.User); 
0

Como otro además de las respuestas anteriores, Zip es generalmente definidos y utilizados en conjunción con un tipo Tuple. Esto alivia al usuario de tener que proporcionar una función resultSelector.

public class Tuple<TItem1, TItem2> // other definitions for higher arity 
{ 
    public TItem1 Item1 { get; private set; } 
    public TItem2 Item2 { get; private set; } 

    public Tuple(TItem1 item1, TItem2 item2) 
    { 
     Item1 = item1; 
     Item2 = item2; 
    } 
} 

Y por lo tanto:

public static IEnumerable<Tuple<TFirst, TSecond>> Zip<TFirst, TSecond> 
    (this IEnumerable<TFirst> first, IEnumerable<TSecond> second) 
{ 
    using (IEnumerator<TFirst> e1 = first.GetEnumerator()) 
    using (IEnumerator<TSecond> e2 = second.GetEnumerator()) 
    { 
     while (e1.MoveNext() && e2.MoveNext()) 
      yield return new Tuple<TFirst, TSecond>(e1.Current, e2.Current); 
    } 
} 

Creo que esto es lo más cerca de CLR 4.0 tendrá (aunque puede tener la variedad más flexible también).

0

Al mirar la respuesta de Marc (y en última instancia el método Zip a partir de .Net 4), hay una cantidad significativa de sobrecarga para enumerar y unir filas donde eventualmente se desechan; ¿Se puede hacer eso sin ese desperdicio?

Al buscar la respuesta de Jon, crear una proyección de entidades dinámicas para referenciar los datos existentes y luego crear un nuevo conjunto de entidades desde ese espejo podría ser un impedimento para usar ese método si el recuento total de filas fuera demasiado grande.

El siguiente fragmento utiliza las referencias a los datos originales y los únicos desperdiciados derechos proyectados creados son los que tienen cadena nula que se eliminan posteriormente. Además, la enumeración de los datos se mantiene al mínimo.

String[] title = { "One","Two","three","Four"}; 
String[] user = { "rob","","john",""}; 

user.Select ((usr, index) => string.IsNullOrEmpty(usr) 
          ? string.Empty 
          : string.Format("{0}:{1}", title[index], usr)) 
    .Where (cmb => string.IsNullOrEmpty(cmb) == false) 

Como un lado, esta metodología podría tener un conjunto de usuarios que es más pequeño en tamaño que el conjunto de títulos como un plus.


La función Aggregate se pasa por alto, aquí está en acción:

int index = 0; 
user.Aggregate (new List<string>(), 
       (result, usr) => 
        { 
         if (string.IsNullOrEmpty(usr) == false) 
          result.Add(string.Format("{0}:{1}", title[index], usr)); 
         ++index; 
         return result; 
         }) 
Cuestiones relacionadas