tags:

views:

852

answers:

4

I have two arrays as below,

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

I need to filter the above array so that I the User value is not Empty. Final Output should be something link { "One:rob","three:john" }

How can I achive this using LINQ

+4  A: 

It sounds like you actually want to "zip" the data together (not join) - i.e. match pairwise; is that correct? If so, simply:

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

using the Zip operation from here:

// 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);
}
Marc Gravell
Oops I have not formed the example well, Values in both the array wont match at all. Please refer the updated Question.
Kusek
I realised that; replaced with "zip"
Marc Gravell
LOL - love the fact that we both used Eric's code.
Jon Skeet
it seems a pretty safe canonical example ;-p
Marc Gravell
Looks like I need to go long way in LINQ. Thanks for the Example that works.
Kusek
Yeah, my goal is to have Marc and Jon post the exact answer that I would have so that I don't have to. Saves me time. Thanks guys!
Eric Lippert
+2  A: 

For a start, you need a Zip operator to zip the two arrays together. Here's an abbreviated version of the code from Eric Lippert's blog (no error checking in this version, just for brevity):

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

Note that Zip will be in the standard libraries for .NET 4.0.

Then you need to just apply a filter and a projection. So we'd get:

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

As a complement to the already posted answers, here is a solution without using the Zip method. This assumes that both arrays is of same length.

        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);
driis
A: 

As another addition to the previous answers, Zip is usually defined and used in conjunction with a Tuple type. This relieves the user of having to provide a resultSelector function.

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

And hence:

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

I believe this is closer to what CLR 4.0 will have (though it may have the more flexible variety as well).

Daniel Earwicker