views:

246

answers:

2

Hey guys, I'm trying to get my head around LINQ and FP, so forgive me if this is naive. I'm trying to do some string parsing using LINQ and mapping onto a function, so I need to split my string up into smaller strings.

I want to split the array up into smaller lists of two. Can I use a reduce (.Aggregate()) to do this? I was trying to work out how to apply the reduce to return a list but I wasn't having any luck.

What I want is:

myString.ToCharArray().Take(2)

Mapped onto every second element.I couldn't get around in my head how to reduce the list without applying the take to every single element, instead of every second one.

Concrete example.

given (1, 2, 3, 4, 5, 6)

I want ((1, 2), (3,4), (5, 6))

To clarify:

given "abcdef"

I want "ab", "cd", "ef"

Cheers for your help guys.

+3  A: 
Enumerable.Range(0, myString.Length)
      .GroupBy(index => index / 2, index => myString[index])
      .Select(eachGroup => new string(eachGroup.ToArray()));

Note that the example above relies on the fact that you have index based access to the collection. To make it work on every IEnumerable<T> without this limitation:

collection.Select((e,i) => new { Index = i, Element = e })
      .GroupBy(x => x.Index / 2, x => x.Element);
Mehrdad Afshari
I was trying to avoid throwing it into lists if I could. The idea was if I could end up with an enumerable of the enumerables of 2, then I could map (.Select()) my conversion over it to create my result.Looks like a good start though.
Spence
Easy, readable and short! Damn! I should start using LINQ more!
SirDemon
Spence: Just remove the ToList, you're done.
Mehrdad Afshari
@SirDemon thats why I'm using LINQ. Using functional programming over lists is just so elegant.
Spence
@Mehrdad. Could you update your answer leaving off the .Select. I realise now that groupBy is returning exactly what I want. I can then just do a .Select there, with eachgroup => myfunction(eachgroup). I'll mark it correct if you make that update.
Spence
@Mehrdad Lovely bit of information after the answer, very elegant. Thanks a bunch for your input :)
Spence
@Mehrdad, I think i see a mistake.Should it be: GroupBy(index => index / 2, index => myString.Substring(index, 2));
Spence
Nope. Test it, it works OK. GroupBy works by processing each element and automatically grouping them if the result of their keys (which is provided by the first function) are equal. The second function passed to GroupBy is the actual element.
Mehrdad Afshari
But this is not what I want.What I need is an enumerable containing substrings of two elements.
Spence
That's pretty easy to do with a select. I'll update it in a moment.
Mehrdad Afshari
myString[index] returns a char, not a pointer into the string, nor does it return two elements.Even if you use substring, you end up with a set of tuples instead of just the list/enumerable of the substrings.
Spence
@Spence: When you get a list of items you want, you can pretty much do anything with them. Like I did with the Select above. Just create a string out of them.
Mehrdad Afshari
I understand now. Each IGrouping element contains two elements, which are the index /2 and index/2 +1 (as integer division ignores remainder). Combining the array will then give you back the two character string.Now I know what an IGrouping is :).
Spence
+1  A: 

This doesn't seem like the approach your looking for, but it's what I see:

Enumerable.Range(0, myString.Length - 1)
    .Where(i => i % 2 == 0)
    .Select(i => myString.Substring(i, 2).ToCharArray());

Note: I think if you have an odd number of characters the substring will probably crash; Probably have to add a check and use something like (myString.Length % 2 == 0 ? 2 : 1), but I haven't tested.

Second Note: To me the difficulty here is that your Reduce actually depends on the array index rather than the values; Hence my solution of enumerating the array indexes and working from there. Another solution would be to provide a method (I can't think of one built in) that enumerates the characters in the string using a tuple that contains the index and the character.

Chris Shaffer
I like it. Lateral. Create a list of array indexes, then create map a substring function from that list using the array.Problem is if you can get it into the functional take format, then you don't need to parse the list each time to substring, you can simply take 2 elements and then map.
Spence