-1

In vb.net How to convert a list of objects within another List of Objects to csv string. I tried below didn't work

String.Join(",", Person.Cars.Select(Function(d) d.Colors.Select(Function(o) o.colorid.ToString)))

Output should be colorid in string csv eg: "101,102,103"

If i try for Car ids that works as expected

String.Join(",", Person.Cars.Select(Function(d) d.carid.ToString)) 

output is "2001,2002,2003"

Here's how data is constructed

   Dim objPerson As New Person
    With objPerson 
    .Cars = new Cars (1) {}
    .Cars(0) = new Cars
    .Cars(1) = new Cars

    With Cars(0)
    .CarId = "2001"

    .Colors = new Colors(1){}
    .Colors(0) = new Colors
    .Colors(1) = new Colors

    With .Colors(0)
    .Colorid = "101"
    End With

    With .Colors(1)
    .Colorid = "102"
    End With

    End With



    With Cars(1)
    .CarId = "2002"

    .Colors = new Colors(1){}
    .Colors(0) = new Colors
    .Colors(1) = new Colors

    With .Colors(0)
    .Colorid = "103"
    End With

    With .Colors(1)
    .Colorid = "104"
    End With

    End With


    End With
End With
Gauls
  • 1,791
  • 5
  • 27
  • 43

2 Answers2

1

LINQ can be a very useful tool. It's not that it allows you to do anything that you couldn't do in other ways. It's just that it allows you do some things in a much easier, cleaner, and more readable way. This is not an example of one of those situations. In this case, your use of LINQ is not only likely to be confusing to others, but it's even confusing to you. I'm all for using LINQ in places where it makes the code easier to read, but, since that's it's only real benefit, I see no reason to use it in places where it makes the code harder to read.

Ok, now that I'm off my high-horse, let me explain your problem. You have two LINQ calls nested inside of each other. The inner one returns a list of color ID's for a car. The outer one calls that inner one once for each car in the list. That means that in the end, you don't have a flat list of color ID's for all of the cars. Rather, you have a 2-D list where there is, essentially, one row for each car and one column for each color ID. You need to flatten it into a 1-D list.

You can do it in LINQ with something like this:

String.Join(",", Person.Cars.Select(Function(d) String.Join(",", d.Colors.Select(Function(o) o.colorid.ToString))))

Or by using SelectMany, as recommended by sloth, but I would argue that you are pushing the bounds of readability and should seriously consider trying to bring your cleverness-factor down a notch and just go with a simpler, more readable, For loop. If that makes me sound old-fashioned, so be it. For instance, I would argue that something like this would cause much less head scratching:

 Private Function GetAllColorIds(cars As IEnumerable(Of Car)) As IEnumerable(Of Integer)
     Dim result As New List(Of Integer)()
     For Each i As Car In cars
         result.AddRange(i.Colors.Select(Function(x) x.colorid.ToString())
     Next
     Return result
 End Function

 ' ...

 Dim csv As String = String.Join(",", GetAllColorIds(Person.Cars))
Steven Doggart
  • 41,612
  • 8
  • 64
  • 99
  • @ Steven : Thanks a ton. I can understand but sometimes it gets difficult to justify when everyone wants to move to linq doesn't matter if "Ferrari racing car is used for grocery shopping". Thanks for you help. – Gauls Feb 17 '15 at 13:47
0

Use SelectMany instead of Select to flatten the result that you pass to String.Join:

String.Join(",", Person.Cars.SelectMany(Function(d) d.Colors.Select(Function(o) o.colorid.ToString)))

In response to your comment:

It seems you're looking for something like

String.Join(",", Person.Cars.Select(Function(d) String.Join("", d.Colors.Select(Function(o) o.colorid.ToString))))
sloth
  • 91,747
  • 17
  • 156
  • 204
  • tired returns "1,0,1,1,0,2,1,0,3" instead of "101,102,103" – Gauls Feb 17 '15 at 13:21
  • @Gauls you should show how you are constructing your data as the above code sample will give you what you want. – Ric Feb 17 '15 at 13:22
  • @Gauls You coud make your question more clear about that. Nonetheless, I've updated my answer. – sloth Feb 17 '15 at 13:26
  • @sloth: Did you miss the common deliberately? Well with comma in the 2nd join it worked. Thanks a ton. String.Join(",", Person.Cars.Select(Function(d) String.Join(",", d.Colors.Select(Function(o) o.colorid.ToString)))) – Gauls Feb 17 '15 at 13:45
  • @Gauls I was unsure how your data actually looks like. – sloth Feb 17 '15 at 13:47
  • @Sloth: Yup can understand , I have updated my question with more info. Cheers – Gauls Feb 17 '15 at 13:50
  • Hi how i ensure o.colorid.ToString is only done when o.colorid <> Nothing tried this String.Join(",", Person.Cars.Select(Function(d) String.Join(",", d.Colors.Select(Function(o) o.colorid <> nothing And o.colorid.ToString)))) – Gauls Feb 20 '15 at 10:45
  • @Gauls Hi. Use a `Where` clause: `...String.Join("", d.Colors.Where(Function(o) o IsNot Nothing).Select(Function(o) o.colorid.ToString))))...`. Consider posting a new question instead of a comment in the future :-) – sloth Feb 20 '15 at 10:50
  • @sloth that worked! Will post new question wasn't sure though. Cheers! – Gauls Feb 20 '15 at 11:08