5

Trying to write a method that splits a list into sublists.

Private Function SplitIdsIntoChunks(ByVal keys As List(Of String)) As List(Of List(Of String))
    Return keys _
        .Select(Function(x, i) New With {Key .Index = i, Key .Value = x}) _
        .GroupBy(Function(x) (x.Index / 10000)) _
        .Select(Function(x) x.Select(Function(v) v.Value).ToList()) _
        .ToList()
End Function

I used C# solution from here. C# solution works fine.

My version written in VB returns a collection of lists with one element instead of 10000. Where did I go wrong?

Thanks in advance.

Edit 1:

Usage:

Dim chunks As List(Of List(Of String)) = SplitIdsIntoChunks(keys)

'Keys' content:

enter image description here

My method returns a lists of lists with one item inside:

enter image description here

Expected result: list of two lists - 10000 items in first and 6256 in second.

Community
  • 1
  • 1
Ghost Master
  • 442
  • 4
  • 12
  • Even though it probably wouldn't be necessary for someone to answer your question in this case, are you able to post what you did in VB.NET? I'm mainly just saying it since you already mentioned a specific error you've run into, so it might decorate the question just a little bit to add that in. – Panzercrisis Mar 17 '15 at 14:45
  • possible duplicate of [Split a collection into n parts with LINQ, in VB.Net](http://stackoverflow.com/questions/28847591/split-a-collection-into-n-parts-with-linq-in-vb-net) – Bjørn-Roger Kringsjå Mar 17 '15 at 15:51

3 Answers3

9

The main 'problem' is that in c# dividing an int by an int, x.Index / 3, results in an integer. Standard division / in VB always results in a Double and therefore a unique Group for each item. e.g.: {.33, .333333, .333334} etc.

The 'trick' is to use integer division \ to get an integer result. I also added a chunksize argument to make it more flexible and reusable:

Private Function SplitIntoChunks(keys As List(Of String), chunkSize As Integer) As List(Of List(Of String))
    Return keys.
            Select(Function(x, i) New With {Key .Index = i, Key .Value = x}).
            GroupBy(Function(x) (x.Index \ chunkSize)).
            Select(Function(x) x.Select(Function(v) v.Value).ToList()).
            ToList()
End Function

One other difference is that c# is very flexible regarding line continuation, so the dot can be at the start or end of a line. VB (since VS2010) supports implicit line continuation after a great many things including member qualifier character (the dot .) so you can get rid of the explicit line continuation (" _").

Usage:

' split list of 1000 into 300 chunks
Dim splits = SplitIntoChunks(myList, 300)

For n As Integer = 0 To splits.Count - 1
    Console.WriteLine("Split {0} has {1} items", n.ToString(), splits(n).Count)
Next

Output/Result:

Split 0 has 300 items
Split 1 has 300 items
Split 2 has 300 items
Splits 3 has 100 items

Note that there is a lot of commentary on the linked C# post about performance of various methods. This just gets it working, some of the alternatives might perform better depending on what is actually being split. The Skip and Take approach used here is interesting too.

Community
  • 1
  • 1
Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
2

The correct translation of that code is:

Private Function SplitIdsIntoChunks(ByVal keys As List(Of String)) As List(Of List(Of String))
    Return keys _
        .Select(Function(x, i) New With {Key .Index = i, Key .Value = x}) _
        .GroupBy(Function(x) (x.Index \ 10000)) _
        .Select(Function(x) x.Select(Function(v) v.Value).ToList()) _
        .ToList()
End Function

Note the difference between / (which returns a floating-point) and \ (which returns an integer).

sloth
  • 99,095
  • 21
  • 171
  • 219
-1

In the linked answer the First Select is like this

.Select((x, i) => new { Index = i, Value = x })

You have in your code

.Select(Function(x, i) New With {Key .Index = i, Key .Value = x})

Why did you repeat Key, I think you only need to specify the Properties of the anonymous type

CheGueVerra
  • 7,849
  • 4
  • 37
  • 49
  • From this [question](http://stackoverflow.com/q/14431601/945456) is sounds like maybe every property in C# is `Key`? Looks like there's some variation between VB and C# in this area. – Jeff B Mar 17 '15 at 14:45
  • The C# code creates an anonymous type with two properties Index and Value, why do you need Key in the VB code. Doesn't work? can you post the code you use and the error message please. You shouldn't need any Key in the VB code – CheGueVerra Mar 17 '15 at 14:53
  • `New With {Key .Index = i, Key .Value = x}` creates an anonymous type with two properties `Index` and `Value`. It's actually good practise to always use the `Key` keyword when working with anonymous types, since it makes it easier to reason how the type behaves. – sloth Mar 17 '15 at 15:24
  • I never had to use that in VB Code...I've always used new With { .PropA = x, .PropB = y} which is like in C# new {PropA = x, PropB = y}. I don't see why I need the Key [keyword] in there. Can someone explain or redirect my search to understand this – CheGueVerra Mar 17 '15 at 15:39
  • For an example, take a look at this question: [Identical (?) C# and VB.NET LINQ queries return different results](https://stackoverflow.com/questions/14928214/identical-c-sharp-and-vb-net-linq-queries-return-different-results) – sloth Mar 18 '15 at 07:51
  • Thanks a million, I had no clue about that, very helpful of you! – CheGueVerra Mar 18 '15 at 11:36