Home All Groups Group Topic Archive Search About

LINQ and data projection

Author
20 Dec 2008 5:28 AM
Gary Nastrasio
I have an array of data and I would like to project every 3 elements
into a new data type.

For example, I have this array:

string[] myString = {"1.0", "2.0", "3.0", "7.0", "8.0", "9.0"};


I would like to project this data into a vertex struct:

struct Vertex
{
    public Vertex(float a, float b, float c){x=a;y=b;z=c;}
    float x, y, z;
};


Here's a for loop that will do it, but I'd like to figure it out with LINQ:

for(int i = 0 ; i < myString.Length ; i += 3)
    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i + 2]);



Thanks for any help.

Author
20 Dec 2008 11:43 AM
Frans Bouma [C# MVP]
Gary Nastrasio wrote:
Show quoteHide quote
> I have an array of data and I would like to project every 3 elements
> into a new data type.
>
> For example, I have this array:
>
> string[] myString = {"1.0", "2.0", "3.0", "7.0", "8.0", "9.0"};
>
>
> I would like to project this data into a vertex struct:
>
> struct Vertex
> {
>     public Vertex(float a, float b, float c){x=a;y=b;z=c;}
>     float x, y, z;
> };
>
>
> Here's a for loop that will do it, but I'd like to figure it out with LINQ:
>
> for(int i = 0 ; i < myString.Length ; i += 3)
>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i + 2]);

    if a 2-line loop statement which is perfectly clear and readable does
the trick, why trying to do it with linq, which likely will also be at
least as complex (if possible at all)?

        FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
Are all your drivers up to date? click for free checkup

Author
20 Dec 2008 3:23 PM
Gary Nastrasio
>>
>> Here's a for loop that will do it, but I'd like to figure it out with
>> LINQ:
>>
>> for(int i = 0 ; i < myString.Length ; i += 3)
>>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i + 2]);
>
>     if a 2-line loop statement which is perfectly clear and readable
> does the trick, why trying to do it with linq, which likely will also be
> at least as complex (if possible at all)?
>
>         FB
>

You're absolutely right about the loop being clear and readable.  I
wanted to try using LINQ for the simple reason of increasing my LINQ
skill level.
Author
21 Dec 2008 10:36 AM
Frans Bouma [C# MVP]
Gary Nastrasio wrote:
Show quoteHide quote
>>>
>>> Here's a for loop that will do it, but I'd like to figure it out with
>>> LINQ:
>>>
>>> for(int i = 0 ; i < myString.Length ; i += 3)
>>>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i + 2]);
>>
>>     if a 2-line loop statement which is perfectly clear and readable
>> does the trick, why trying to do it with linq, which likely will also
>> be at least as complex (if possible at all)?
>>
>>         FB
>>
>
> You're absolutely right about the loop being clear and readable.  I
> wanted to try using LINQ for the simple reason of increasing my LINQ
> skill level.

    Heh, then this particular task will definitely increase your skill
level, because IMHO it's pretty hard to do, at first glance. I haven't
looked at it more closely, but I think it's pretty hard to do this in
linq, because of the skipping part you have to do after you've read 3
elements from the sequence. I can think of a way with ElementAt(n), but
that is extremely inefficient (it seeks with every element), or with a
3-query process where you project the top element of the sequence into a
new anonymous type together with the rest of the sequence (although that
also sounds like impossible).

    I've done a lot of linq-to-objects queries in the past months and my
experience is that for some things it's really handy, and one should
definitely use it for these situations, but in other situations, like
where it takes a while before you see how it should be done, it's often
easier to simply go for a loop, as that gets the thing done as well.

        FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
Author
21 Dec 2008 2:49 PM
Anthony Jones
Show quote Hide quote
"Frans Bouma [C# MVP]" <perseus.usenetNOSPAM@xs4all.nl> wrote in message
news:OOvkJg1YJHA.4480@TK2MSFTNGP06.phx.gbl...
> Gary Nastrasio wrote:
>>>>
>>>> Here's a for loop that will do it, but I'd like to figure it out with
>>>> LINQ:
>>>>
>>>> for(int i = 0 ; i < myString.Length ; i += 3)
>>>>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i +
>>>> 2]);
>>>
>>>     if a 2-line loop statement which is perfectly clear and readable
>>> does the trick, why trying to do it with linq, which likely will also be
>>> at least as complex (if possible at all)?
>>>
>>>         FB
>>>
>>
>> You're absolutely right about the loop being clear and readable.  I
>> wanted to try using LINQ for the simple reason of increasing my LINQ
>> skill level.
>
> Heh, then this particular task will definitely increase your skill level,
> because IMHO it's pretty hard to do, at first glance. I haven't looked at
> it more closely, but I think it's pretty hard to do this in linq, because
> of the skipping part you have to do after you've read 3 elements from the
> sequence. I can think of a way with ElementAt(n), but that is extremely
> inefficient (it seeks with every element), or with a 3-query process where
> you project the top element of the sequence into a new anonymous type
> together with the rest of the sequence (although that also sounds like
> impossible).
>
> I've done a lot of linq-to-objects queries in the past months and my
> experience is that for some things it's really handy, and one should
> definitely use it for these situations, but in other situations, like
> where it takes a while before you see how it should be done, it's often
> easier to simply go for a loop, as that gets the thing done as well.
>

This is an interesting problem though and its definitely a good exercise
that can help broaden ones knowledge of LINQ.

I've been trying to solve this for the general case where all that is known
of the data source is that its an IEnumerable<T>.  In this case indexers are
not available.  Of course direct usuage of the Enumerator returned by
GetEnumerator could be used but I'm wondering if a solution can be described
in LINQ or with the extension methods and Lambda's.

So far I've failed miserably.

--
Anthony Jones - MVP ASP/ASP.NET
Author
21 Dec 2008 4:27 PM
Anthony Jones
Show quote Hide quote
"Anthony Jones" <AnthonyWJo***@yadayadayada.com> wrote in message
news:%23K$DYt3YJHA.4852@TK2MSFTNGP04.phx.gbl...
> "Frans Bouma [C# MVP]" <perseus.usenetNOSPAM@xs4all.nl> wrote in message
> news:OOvkJg1YJHA.4480@TK2MSFTNGP06.phx.gbl...
>> Gary Nastrasio wrote:
>>>>>
>>>>> Here's a for loop that will do it, but I'd like to figure it out with
>>>>> LINQ:
>>>>>
>>>>> for(int i = 0 ; i < myString.Length ; i += 3)
>>>>>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i >
> This is an interesting problem though and its definitely a good exercise
> that can help broaden ones knowledge of LINQ.
>
> I've been trying to solve this for the general case where all that is
> known > of the data source is that its an IEnumerable<T>.  In this case
> indexers are not available.  Of course direct usuage of the Enumerator
> returned by GetEnumerator could be used but I'm wondering if a solution
> can be described in LINQ or with the extension methods and Lambda's.
>
> So far I've failed miserably.
>

This seems pretty sucessful and readable:-

  struct Vertex
  {
   public int x { get; private set; }
   public int y { get; private set; }
   public int z { get; private set; }
   public Vertex(int a, int b, int c) : this() { x = a; y = b; z = c; }
  }

    var x = from i in Enumerable.Range(1, 9)
        group i by (i - 1) / 3 into g
        let v = g.ToArray()
        select new Vertex(v[0], v[1], v[2]);

    foreach (var v in x)
     Console.WriteLine("x: {0}, y: {1}, z: {2}", v.x, v.y, v.z);

It would be nice to avoid the ToArray() though.

--
Anthony Jones - MVP ASP/ASP.NET
Author
21 Dec 2008 7:39 PM
Jeroen Mostert
Anthony Jones wrote:
Show quoteHide quote
> "Anthony Jones" <AnthonyWJo***@yadayadayada.com> wrote in message
> news:%23K$DYt3YJHA.4852@TK2MSFTNGP04.phx.gbl...
>> "Frans Bouma [C# MVP]" <perseus.usenetNOSPAM@xs4all.nl> wrote in
>> message news:OOvkJg1YJHA.4480@TK2MSFTNGP06.phx.gbl...
>>> Gary Nastrasio wrote:
>>>>>>
>>>>>> Here's a for loop that will do it, but I'd like to figure it out
>>>>>> with LINQ:
>>>>>>
>>>>>> for(int i = 0 ; i < myString.Length ; i += 3)
>>>>>>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i >
>> This is an interesting problem though and its definitely a good
>> exercise that can help broaden ones knowledge of LINQ.
>>
>> I've been trying to solve this for the general case where all that is
>> known > of the data source is that its an IEnumerable<T>.  In this
>> case indexers are not available.  Of course direct usuage of the
>> Enumerator returned by GetEnumerator could be used but I'm wondering
>> if a solution can be described in LINQ or with the extension methods
>> and Lambda's.
>>
>> So far I've failed miserably.
>>
>
> This seems pretty sucessful and readable:-
>
>  struct Vertex
>  {
>   public int x { get; private set; }
>   public int y { get; private set; }
>   public int z { get; private set; }
>   public Vertex(int a, int b, int c) : this() { x = a; y = b; z = c; }
>  }
>
>    var x = from i in Enumerable.Range(1, 9)
>        group i by (i - 1) / 3 into g
>        let v = g.ToArray()
>        select new Vertex(v[0], v[1], v[2]);
>
>    foreach (var v in x)
>     Console.WriteLine("x: {0}, y: {1}, z: {2}", v.x, v.y, v.z);
>
> It would be nice to avoid the ToArray() though.
>
Easy enough, though it's hardly elegant:

   var flatVertices = new double[] { 1.0, 2.0, 3.0, 7.0, 8.0, 9.0 };
   var vertices =
     from v in flatVertices.Select((f, i) => new { f, i = i / 3 })
     group v by v.i into g
     let x = g.First().f
     let yg = g.Skip(1)
     let y = yg.First().f
     let zg = yg.Skip(1)
     let z = zg.First().f
     select new Vertex(x, y, z);

It's not really possible to do this elegantly in LINQ (OK, so you might get
something if you throw in recursion, but I honestly wouldn't bother). Use a
real functional language like F#, so you get the benefits of arbitrary
tuples and pattern matching.

--
J.
Author
21 Dec 2008 10:07 PM
Anthony Jones
Show quote Hide quote
"Jeroen Mostert" <jmost***@xs4all.nl> wrote in message
news:494e9b56$0$185$e4fe514c@news.xs4all.nl...
> Anthony Jones wrote:
>> "Anthony Jones" <AnthonyWJo***@yadayadayada.com> wrote in message
>> news:%23K$DYt3YJHA.4852@TK2MSFTNGP04.phx.gbl...
>>> "Frans Bouma [C# MVP]" <perseus.usenetNOSPAM@xs4all.nl> wrote in message
>>> news:OOvkJg1YJHA.4480@TK2MSFTNGP06.phx.gbl...
>>>> Gary Nastrasio wrote:
>>>>>>>
>>>>>>> Here's a for loop that will do it, but I'd like to figure it out
>>>>>>> with LINQ:
>>>>>>>
>>>>>>> for(int i = 0 ; i < myString.Length ; i += 3)
>>>>>>>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i >
>>> This is an interesting problem though and its definitely a good exercise
>>> that can help broaden ones knowledge of LINQ.
>>>
>>> I've been trying to solve this for the general case where all that is
>>> known > of the data source is that its an IEnumerable<T>.  In this case
>>> indexers are not available.  Of course direct usuage of the Enumerator
>>> returned by GetEnumerator could be used but I'm wondering if a solution
>>> can be described in LINQ or with the extension methods and Lambda's.
>>>
>>> So far I've failed miserably.
>>>
>>
>> This seems pretty sucessful and readable:-
>>
>>  struct Vertex
>>  {
>>   public int x { get; private set; }
>>   public int y { get; private set; }
>>   public int z { get; private set; }
>>   public Vertex(int a, int b, int c) : this() { x = a; y = b; z = c; }
>>  }
>>
>>    var x = from i in Enumerable.Range(1, 9)
>>        group i by (i - 1) / 3 into g
>>        let v = g.ToArray()
>>        select new Vertex(v[0], v[1], v[2]);
>>
>>    foreach (var v in x)
>>     Console.WriteLine("x: {0}, y: {1}, z: {2}", v.x, v.y, v.z);
>>
>> It would be nice to avoid the ToArray() though.
>>
> Easy enough, though it's hardly elegant:
>
>   var flatVertices = new double[] { 1.0, 2.0, 3.0, 7.0, 8.0, 9.0 };
>   var vertices =
>     from v in flatVertices.Select((f, i) => new { f, i = i / 3 })
>     group v by v.i into g
>     let x = g.First().f
>     let yg = g.Skip(1)
>     let y = yg.First().f
>     let zg = yg.Skip(1)
>     let z = zg.First().f
>     select new Vertex(x, y, z);
>
> It's not really possible to do this elegantly in LINQ

If elegance was the only criteria then the my existing attempt is pretty
good, its just that it would be nice not be creating and throwing away an
array per vertex.

>(OK, so you might get something if you throw in recursion, but I honestly
>wouldn't bother). >Use a real functional language like F#, so you get the
>benefits of arbitrary tuples and pattern matching.
>

F# may be a solution if someone could find a way to explain it properly to
non-functional thinkers.  What would an F# solution look like?

--
Anthony Jones - MVP ASP/ASP.NET
Author
21 Dec 2008 10:18 PM
Anthony Jones
Show quote Hide quote
"Anthony Jones" <AnthonyWJo***@yadayadayada.com> wrote in message
news:%23Qsp4h7YJHA.3548@TK2MSFTNGP05.phx.gbl...
> "Jeroen Mostert" <jmost***@xs4all.nl> wrote in message
> news:494e9b56$0$185$e4fe514c@news.xs4all.nl...
>> Anthony Jones wrote:
>>> "Anthony Jones" <AnthonyWJo***@yadayadayada.com> wrote in message
>>> news:%23K$DYt3YJHA.4852@TK2MSFTNGP04.phx.gbl...
>>>> "Frans Bouma [C# MVP]" <perseus.usenetNOSPAM@xs4all.nl> wrote in
>>>> message news:OOvkJg1YJHA.4480@TK2MSFTNGP06.phx.gbl...
>>>>> Gary Nastrasio wrote:
>>>>>>>>
>>>>>>>> Here's a for loop that will do it, but I'd like to figure it out
>>>>>>>> with LINQ:
>>>>>>>>
>>>>>>>> for(int i = 0 ; i < myString.Length ; i += 3)
>>>>>>>>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i >
>>>> This is an interesting problem though and its definitely a good
>>>> exercise that can help broaden ones knowledge of LINQ.
>>>>
>>>> I've been trying to solve this for the general case where all that is
>>>> known > of the data source is that its an IEnumerable<T>.  In this case
>>>> indexers are not available.  Of course direct usuage of the Enumerator
>>>> returned by GetEnumerator could be used but I'm wondering if a solution
>>>> can be described in LINQ or with the extension methods and Lambda's.
>>>>
>>>> So far I've failed miserably.
>>>>
>>>
>>> This seems pretty sucessful and readable:-
>>>
>>>  struct Vertex
>>>  {
>>>   public int x { get; private set; }
>>>   public int y { get; private set; }
>>>   public int z { get; private set; }
>>>   public Vertex(int a, int b, int c) : this() { x = a; y = b; z = c; }
>>>  }
>>>
>>>    var x = from i in Enumerable.Range(1, 9)
>>>        group i by (i - 1) / 3 into g
>>>        let v = g.ToArray()
>>>        select new Vertex(v[0], v[1], v[2]);
>>>
>>>    foreach (var v in x)
>>>     Console.WriteLine("x: {0}, y: {1}, z: {2}", v.x, v.y, v.z);
>>>
>>> It would be nice to avoid the ToArray() though.
>>>
>> Easy enough, though it's hardly elegant:
>>
>>   var flatVertices = new double[] { 1.0, 2.0, 3.0, 7.0, 8.0, 9.0 };
>>   var vertices =
>>     from v in flatVertices.Select((f, i) => new { f, i = i / 3 })
>>     group v by v.i into g
>>     let x = g.First().f
>>     let yg = g.Skip(1)
>>     let y = yg.First().f
>>     let zg = yg.Skip(1)
>>     let z = zg.First().f
>>     select new Vertex(x, y, z);
>>
>> It's not really possible to do this elegantly in LINQ
>
> If elegance was the only criteria then the my existing attempt is pretty
> good, its just that it would be nice not be creating and throwing away an
> array per vertex.
>

Actually taking a look at your code again it does fix a bug in my solution
where the grouping value is all wrong.


--
Anthony Jones - MVP ASP/ASP.NET
Author
20 Dec 2008 11:37 PM
Arne_Vajhøj
Gary Nastrasio wrote:
Show quoteHide quote
> I have an array of data and I would like to project every 3 elements
> into a new data type.
>
> For example, I have this array:
>
> string[] myString = {"1.0", "2.0", "3.0", "7.0", "8.0", "9.0"};
>
>
> I would like to project this data into a vertex struct:
>
> struct Vertex
> {
>     public Vertex(float a, float b, float c){x=a;y=b;z=c;}
>     float x, y, z;
> };
>
>
> Here's a for loop that will do it, but I'd like to figure it out with LINQ:
>
> for(int i = 0 ; i < myString.Length ; i += 3)
>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i + 2]);

LINQ does not seem to be the best tool to transform:

1.0
2.0
3.0
7.0
8.0
9.0

to:

1.0 2.0 3.0
7.0 8.0 9.0

which is the core of the problem.

Arne
Author
22 Dec 2008 1:51 AM
Michael C
"Gary Nastrasio" <garynastra***@hotmail.com> wrote in message
news:eFQaXPmYJHA.5108@TK2MSFTNGP05.phx.gbl...
> Here's a for loop that will do it, but I'd like to figure it out with
> LINQ:
>
> for(int i = 0 ; i < myString.Length ; i += 3)
>    Vertex v = new Vertex(myString[i], myString[i + 1], myString[i + 2]);

Linq has a lot of holes that are not that difficult to come accross. If
you're going to use it you need to get used to the idea of adding your own
linq functions. In this case you could write a function to grab a certain
numer of elements from the source. The end result would look something like
this. Note the conversion to integer which your original code doesn't have.

myString.Select(i => Convert.ToInt32(i)).GrabChunk((i, j, k) => new
Vertex(i, j, k));



The function would look something like this. Note I don't have vs2008 here
to test this so some fixes will undoubtedly be needed. We can write a
function for 2, 3 and 4 arguements, for obvious reasons we don't need one
for 1 arguement :-)


//for 2 arguements:
public static IEnumerable<TResult> GrabChunk<TIn, TResult>(this
IEnumerable<TIn> source, Func<TIn, TIn, TResult> selector)
{
    IEnumerator<TIn> en = source.GetEnumerator();
    TIn a, b;
    if(en.MoveNext())
    {
        a = en.Current;
        if(en.MoveNext())
        {
            b = en.Current;
            yield return selector(a, b);
        }
    }
}

//for 3 arguements:
public static IEnumerable<TResult> GrabChunk<TIn, TResult>(IEnumerable<TIn>
source, Func<TIn, TIn, TIn, TResult> selector)
{
    IEnumerator<TIn> en = source.GetEnumerator();
    TIn a, b, c;
    if(en.MoveNext())
    {
        a = en.Current;
        if(en.MoveNext())
        {
            b = en.Current;
            if(en.MoveNext())
            {
                c = en.Current;
                yield return selector(a, b, c);
            }
        }
    }
}

Show quoteHide quote
>
>
>
> Thanks for any help.

Bookmark and Share