LINQ “zip” in Array di stringhe

Diciamo che ci sono due array:

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

Ho bisogno di filtrare l’array sopra quando il valore user è vuoto quindi unire o comprimere i due insieme. L’output finale dovrebbe essere come:

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

Come si può fare usando LINQ?

Sembra che tu voglia davvero “comprimere” i dati insieme (non unirti) – cioè abbinare a coppie; è corretto? Se è così, semplicemente:

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

usando l’operazione Zip da qui :

 // http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx public static IEnumerable Zip (this IEnumerable first, IEnumerable second, Func 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 ZipIterator (IEnumerable first, IEnumerable second, Func resultSelector) { using (IEnumerator e1 = first.GetEnumerator()) using (IEnumerator e2 = second.GetEnumerator()) while (e1.MoveNext() && e2.MoveNext()) yield return resultSelector(e1.Current, e2.Current); } 

Per cominciare, è necessario un operatore Zip per comprimere i due array. Ecco una versione abbreviata del codice dal blog di Eric Lippert (nessun controllo degli errori in questa versione, solo per brevità):

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

Nota che Zip sarà nelle librerie standard per .NET 4.0.

Quindi devi solo applicare un filtro e una proiezione. Quindi otterremmo:

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

Come complemento delle risposte già pubblicate, ecco una soluzione senza utilizzare il metodo Zip. Ciò presuppone che entrambi gli array abbiano la stessa lunghezza.

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

Come ulteriore aggiunta alle risposte precedenti, Zip viene generalmente definito e utilizzato in combinazione con un tipo Tuple . Questo allevia l’utente di dover fornire una funzione resultSelector .

 public class Tuple // 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; } } 

E quindi:

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

Credo che questo sia più vicino a ciò che CLR 4.0 avrà (sebbene possa avere anche la varietà più flessibile).

Osservando la risposta di Marc (e in definitiva il metodo Zip da .Net 4), c’è una quantità significativa di spese generali per enumerare e unire le righe dove vengono gettate via; può essere fatto senza quei rifiuti?

Guardando la risposta di Jon, creare una proiezione di quadro dinamiche per fare riferimento ai dati esistenti e quindi creare una nuova serie di quadro da quel mirror potrebbe essere un deterrente per l’utilizzo di tale metodo se il numero totale di righe fosse troppo grande.

Il frammento di seguito utilizza i riferimenti ai dati originali e le uniche autorizzazioni proiettate sprecate create sono quelle che hanno una stringa nulla che viene successivamente rimossa. Anche l’enumerazione dei dati è ridotta al minimo.

 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) 

Per inciso, questa metodologia potrebbe avere un array utente di dimensioni inferiori rispetto all’array del titolo come plus.


La funzione Aggregate è trascurata, qui è in azione:

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