views:

91

answers:

3

HI.

This is what I want to do:

    str2 = "91";
    str1 = "19";
    var testQuery = from c1 in str1
                    from c2 in str2
                    select new {c1, c2};
    foreach (var enumerable in testQuery)
    {
        Console.WriteLine(enumerable.c1 + " | " + enumerable.c2);
    }

What I want:

9 | 1
1 | 9

What I really get:

1 | 9
1 | 1
9 | 9
9 | 1

The code is a pure example. It might iterate through arrays or some other collection. It will also do some other things, but they are irrelevant to this problem.

Without linq, I could do something like this:

    for (int i = 0; i < str1.Length -1; i++)
    {
        Console.WriteLine(str1[i] + " | " + str2[i]);
    }

But I want a query that can do everything I need instead.

Do I really have to create my own enumerator method that uses yield to create what I want?

EDIT: per request: An example of what I'd like to be able to do:

    str1 = "91";
    str2 = "19";

    char[] digitX = str1.ToString().Reverse().ToArray();
    char[] digitY = str2.ToString().Reverse().ToArray();

    var q = digitX.Select((c1, i) => new {c1 = c1 - '0', c2 = digitY[i] - '0' });

I'd like to pull the reverse etc. in the actual query. So I keep it all gathered. Extra: Being able to pull this with the sql-like sugarcoated syntax, I'd be thrilled. Instead of a method chain, that is.

+1  A: 

Use Enumerable.Zip. Your first example can be rewritten from:

str2 = "91";
str1 = "19";
var testQuery = from c1 in str1
                from c2 in str2
                select new {c1, c2};
foreach (var enumerable in testQuery)
{
    Console.WriteLine(enumerable.c1 + " | " + enumerable.c2);
}

to

str2 = "91";
str1 = "19";
var strings = Enumerable.Zip(c1, c2, (a, b) => a + " | " + b);
foreach (var str in strings)
{
    Console.WriteLine(str);
}
Doug McClean
Ah, I did recall something about zip, but I did not investigate it, since it is only availiable in the .Net 4 Framework, which is in beta.Can this be solved in 3.5?
CasperT
Perfect, but it's .NET 4 only, though.
Reed Copsey
@CasperT: My answer uses select with indexing to do a zip in 3.5sp1
Reed Copsey
Whoops, you're right, sorry. I just thought about how I would do this in Haskell, Google'd the corresponding LINQ function, and posted it. I didn't realize it was added for version 4. Sorry. I will post the code to roll your own.
Doug McClean
(not a true zip, though, since it's relying on string's indexer)
Reed Copsey
I added another answer showing how to recreate the Zip function on your own in 3.5. Sorry for the confusion, guys.
Doug McClean
+1  A: 

You can use:

var testQuery = str1.Select( (c,i) => new {c, str2[i]} );

Edit:

Given your new question, you should be able to do:

var q = str1.Reverse().Select((c1, i) => new { c1 = c1 - '0', c2 = str2[str2.Length - i - 1] - '0' });
Reed Copsey
That is cute and I have no idea why I didn't think of that!For reference, I have written:str1.Select( (c1,i) => new {c1, c2 = str2[i]} );
CasperT
I know this is perhaps out of scope, compared to my question. But what if I wanted to imply some manipulation of str2? Would I HAVE to do this before(outside the query) calling the query you have made?
CasperT
I can throw in an example, if you are confused
CasperT
Depends on the manipulation - but in general, no, you can do anything you want inside of the Select() method - ie: str1.Select( (c,i) => new {c1 = c, c2 = Char.ToUpper(str2[i]) }; -- Or did you mean something different?
Reed Copsey
Oh, I am sorry to hear that - and you don't think there is any alternatives to your code? in c# 3.5 without making your own enumable method
CasperT
What did you want to do inside there? Can you edit your question to be more specific? - there's probably a way to do it...
Reed Copsey
I added an example. I hope it helps. I will accept your answer if you figure out a good solution :)
CasperT
I just put in an answer for you - does what you were asking for
Reed Copsey
Too clever to ignore :) Also, what do you call i? just for the case of curiousity. Default indexer?
CasperT
Usually something like indexer. It is documented as "the index of the source element" on http://msdn.microsoft.com/en-us/library/bb534869.aspx
Reed Copsey
+3  A: 

My other answer is less useful than it could be, because the Enumerable.Zip function was added in version 4.

So, here's how to roll your own:

public static class EnumerableExtensions
{
    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
                                           IEnumerable<TFirst> first,
                                           IEnumerable<TSecond> second,
                                           Func<TFirst, TSecond, TResult> resultSelector)
    {
        using(var firstEnum = first.GetEnumerator())
        using(var secondEnum = second.GetEnumerator())
        {
            while(firstEnum.MoveNext() && secondEnum.MoveNext())
            {
                yield return resultSelector(firstEnum.Current, secondEnum.Current);
            }
        }
    }
}
Doug McClean
Thanks. I can't accept it as an answer though, since I am looking for a way to do it, without having my own enumarable method :) I appreciate the effort though!
CasperT
Ooops, I missed that part of the question, sorry. Oh well, maybe it will be useful to someone.
Doug McClean