Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. Other Discussions
  3. Clever Code
  4. LINQ gotcha I ran into

LINQ gotcha I ran into

Scheduled Pinned Locked Moved Clever Code
helpcsharplinqannouncement
13 Posts 8 Posters 4 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Kevin McFarlane

    Slick! :) Btw, in the first version you have a typo. The second SkipWhile should be TakeWhile. I translated this to VB and it works fine. I normally do C# but I'm currently in a short contract using VB. Some things seem not to carry across - or I haven't figured out how. In my real code I'll have to do this with objects, not numbers and the ordering will be by dates and times.

    Kevin

    E Offline
    E Offline
    Ed Poore
    wrote on last edited by
    #4

    Kevin McFarlane wrote:

    in the first version you have a typo

    Oops, I switched them around to make more sense with what I'd called the variables but obviously missed one part of it. I'm sure there's an even better way but best I can think of at the moment.


    I doubt it. If it isn't intuitive then we need to fix it. - Chris Maunder

    1 Reply Last reply
    0
    • K Kevin McFarlane

      This is a simplified version of a real problem I had at work on Friday. I have these numbers 5, 4, 12, 0, 8, 5, 14, 0, 37 I want to arrange them thus 4, 5, 5, 8, 12, 14, 37, 0, 0 i.e., numbers in ascending order but with 0s moved to end. Initially tried this

      Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
      Dim numbers As New List(Of Integer)(data)
      Console.WriteLine("Before")
      For Each e In numbers
      Console.WriteLine(e)
      Next

      ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

      Dim extract = numbers.Where(Function(x) x = 0)
      Console.WriteLine("Extract")
      For Each e In extract
      Console.WriteLine(e)
      Next

      numbers.RemoveAll(Function(x) x = 0)
      numbers.Sort()
      numbers.AddRange(extract)
      Console.WriteLine("After")
      For Each e In numbers
      Console.WriteLine(e)
      Next

      But results were unexpected Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 i.e., extract is empty after numbers.RemoveAll. What I needed to do was this

      Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
      Dim numbers As New List(Of Integer)(data)
      Console.WriteLine("Before")
      For Each e In numbers
      Console.WriteLine(e)
      Next

      ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

      Dim extract = numbers.Where(Function(x) x = 0).ToList()
      Console.WriteLine("Extract")
      For Each e In extract
      Console.WriteLine(e)
      Next

      numbers.RemoveAll(Function(x) x = 0)
      numbers.Sort()
      numbers.AddRange(extract)
      Console.WriteLine("After")
      For Each e In numbers
      Console.WriteLine(e)
      Next

      i.e., first take a copy of extract as a list before appending. New results Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 0 0 :-O Now tomorrow I should be able to fix my real code. Btw, if anyone has a more elegant solution to this feel free to offer it. I'm always willing to learn. :) Kevin

      Kevin

      L Offline
      L Offline
      Lost User
      wrote on last edited by
      #5

      Fun, but I would just use Sort with a comparer anonymous method/delegate that sort zero above all other values..

      L 1 Reply Last reply
      0
      • L Lost User

        Fun, but I would just use Sort with a comparer anonymous method/delegate that sort zero above all other values..

        L Offline
        L Offline
        Lost User
        wrote on last edited by
        #6

        Yeah, that's what I'd also do. Makes the code more readable than the LINQ-Version.

        1 Reply Last reply
        0
        • K Kevin McFarlane

          This is a simplified version of a real problem I had at work on Friday. I have these numbers 5, 4, 12, 0, 8, 5, 14, 0, 37 I want to arrange them thus 4, 5, 5, 8, 12, 14, 37, 0, 0 i.e., numbers in ascending order but with 0s moved to end. Initially tried this

          Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
          Dim numbers As New List(Of Integer)(data)
          Console.WriteLine("Before")
          For Each e In numbers
          Console.WriteLine(e)
          Next

          ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

          Dim extract = numbers.Where(Function(x) x = 0)
          Console.WriteLine("Extract")
          For Each e In extract
          Console.WriteLine(e)
          Next

          numbers.RemoveAll(Function(x) x = 0)
          numbers.Sort()
          numbers.AddRange(extract)
          Console.WriteLine("After")
          For Each e In numbers
          Console.WriteLine(e)
          Next

          But results were unexpected Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 i.e., extract is empty after numbers.RemoveAll. What I needed to do was this

          Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
          Dim numbers As New List(Of Integer)(data)
          Console.WriteLine("Before")
          For Each e In numbers
          Console.WriteLine(e)
          Next

          ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

          Dim extract = numbers.Where(Function(x) x = 0).ToList()
          Console.WriteLine("Extract")
          For Each e In extract
          Console.WriteLine(e)
          Next

          numbers.RemoveAll(Function(x) x = 0)
          numbers.Sort()
          numbers.AddRange(extract)
          Console.WriteLine("After")
          For Each e In numbers
          Console.WriteLine(e)
          Next

          i.e., first take a copy of extract as a list before appending. New results Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 0 0 :-O Now tomorrow I should be able to fix my real code. Btw, if anyone has a more elegant solution to this feel free to offer it. I'm always willing to learn. :) Kevin

          Kevin

          L Offline
          L Offline
          Lutoslaw
          wrote on last edited by
          #7

          As Harold has suggested, it's better to just sort it using a simple comrasion formula defined as below:

          (a, b) => a == 0 ? 1 : a - b

          There is no ?: in VB, so

          if (a == 0)
          return 1;
          else
          return a - b;

          Array.Sort(numbers, (a, b) => a == 0 ? 1 : a - b);

          In the Real World:

          Array.Sort(data, (a, b) => a.Equals(ZERO_DEFINITION) ? 1 : a.CompareTo(b);

          EDIT: Looking at your code, It seems that a VB version would be somthing like his:

          Array.Sort(numbers,
          Function(a, b)
          If a == 0 Then
          return 1
          else
          return a - b);

          Greetings - Gajatko Portable.NET is part of DotGNU, a project to build a complete Free Software replacement for .NET - a system that truly belongs to the developers.

          A 1 Reply Last reply
          0
          • E Ed Poore

            Not in Visual Basic .NET but a more elegant solution using LINQ I would think is:

            int[] data = new int[] { 5, 4, 12, 0, 8, 5, 14, 0, 37 };
            var ordered = data.OrderBy(k => k);

            var nonZero = ordered.SkipWhile((value, index) => (value == 0));
            var zeroes = ordered.SkipWhile((value, index) => (value == 0));
            var finished = nonZero.Concat(zeroes);

            Or for a more compact version:

            var finished = ordered.SkipWhile((v, i) => (v == 0)).Concat(ordered.TakeWhile((v, i) => (v == 0)));


            I doubt it. If it isn't intuitive then we need to fix it. - Chris Maunder

            S Offline
            S Offline
            singh iz king
            wrote on last edited by
            #8

            What about ?

            var finished = ordered.Where(n => n != 0).Concat(ordered.Where(n => n == 0))

            What is the benefit of using SkipWhile and TakeWhile over just using the Where clause?

            E 1 Reply Last reply
            0
            • S singh iz king

              What about ?

              var finished = ordered.Where(n => n != 0).Concat(ordered.Where(n => n == 0))

              What is the benefit of using SkipWhile and TakeWhile over just using the Where clause?

              E Offline
              E Offline
              Ed Poore
              wrote on last edited by
              #9

              I would assume that the SkipWhile and TakeWhile will be a bit faster because Where is going to compare every single element in the array. Using TakeWhile it'll compare n+1 where n is the number of 0s. Then SkipWhile will compare n+1 again until it hits a non-zero and then copies everything else. Since we know that the 0s in finished will be at the beginning there's no need to compare all the elements in the array. I think anyway (I'm not that much of an expert on LINQ) :rolleyes:


              I doubt it. If it isn't intuitive then we need to fix it. - Chris Maunder

              1 Reply Last reply
              0
              • K Kevin McFarlane

                This is a simplified version of a real problem I had at work on Friday. I have these numbers 5, 4, 12, 0, 8, 5, 14, 0, 37 I want to arrange them thus 4, 5, 5, 8, 12, 14, 37, 0, 0 i.e., numbers in ascending order but with 0s moved to end. Initially tried this

                Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
                Dim numbers As New List(Of Integer)(data)
                Console.WriteLine("Before")
                For Each e In numbers
                Console.WriteLine(e)
                Next

                ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

                Dim extract = numbers.Where(Function(x) x = 0)
                Console.WriteLine("Extract")
                For Each e In extract
                Console.WriteLine(e)
                Next

                numbers.RemoveAll(Function(x) x = 0)
                numbers.Sort()
                numbers.AddRange(extract)
                Console.WriteLine("After")
                For Each e In numbers
                Console.WriteLine(e)
                Next

                But results were unexpected Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 i.e., extract is empty after numbers.RemoveAll. What I needed to do was this

                Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
                Dim numbers As New List(Of Integer)(data)
                Console.WriteLine("Before")
                For Each e In numbers
                Console.WriteLine(e)
                Next

                ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

                Dim extract = numbers.Where(Function(x) x = 0).ToList()
                Console.WriteLine("Extract")
                For Each e In extract
                Console.WriteLine(e)
                Next

                numbers.RemoveAll(Function(x) x = 0)
                numbers.Sort()
                numbers.AddRange(extract)
                Console.WriteLine("After")
                For Each e In numbers
                Console.WriteLine(e)
                Next

                i.e., first take a copy of extract as a list before appending. New results Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 0 0 :-O Now tomorrow I should be able to fix my real code. Btw, if anyone has a more elegant solution to this feel free to offer it. I'm always willing to learn. :) Kevin

                Kevin

                C Offline
                C Offline
                Charles E Wagner Jr
                wrote on last edited by
                #10

                Kevin what you see as a subtle bug is a typical mistake made by programmers new to Linq that haven't read the MSDN Linq documentation(specifically Deferred Query Evaluation). The method call here causes immediate execution from within the ToList method. Dim extract = numbers.Where(Function(x) x = 0).ToList()

                Charles E. Wagner Jr.

                K 1 Reply Last reply
                0
                • C Charles E Wagner Jr

                  Kevin what you see as a subtle bug is a typical mistake made by programmers new to Linq that haven't read the MSDN Linq documentation(specifically Deferred Query Evaluation). The method call here causes immediate execution from within the ToList method. Dim extract = numbers.Where(Function(x) x = 0).ToList()

                  Charles E. Wagner Jr.

                  K Offline
                  K Offline
                  Kevin McFarlane
                  wrote on last edited by
                  #11

                  Yeah, I remember coming across that a while back but didn't really take it in.

                  1 Reply Last reply
                  0
                  • K Kevin McFarlane

                    This is a simplified version of a real problem I had at work on Friday. I have these numbers 5, 4, 12, 0, 8, 5, 14, 0, 37 I want to arrange them thus 4, 5, 5, 8, 12, 14, 37, 0, 0 i.e., numbers in ascending order but with 0s moved to end. Initially tried this

                    Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
                    Dim numbers As New List(Of Integer)(data)
                    Console.WriteLine("Before")
                    For Each e In numbers
                    Console.WriteLine(e)
                    Next

                    ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

                    Dim extract = numbers.Where(Function(x) x = 0)
                    Console.WriteLine("Extract")
                    For Each e In extract
                    Console.WriteLine(e)
                    Next

                    numbers.RemoveAll(Function(x) x = 0)
                    numbers.Sort()
                    numbers.AddRange(extract)
                    Console.WriteLine("After")
                    For Each e In numbers
                    Console.WriteLine(e)
                    Next

                    But results were unexpected Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 i.e., extract is empty after numbers.RemoveAll. What I needed to do was this

                    Dim data() As Integer = {5, 4, 12, 0, 8, 5, 14, 0, 37}
                    Dim numbers As New List(Of Integer)(data)
                    Console.WriteLine("Before")
                    For Each e In numbers
                    Console.WriteLine(e)
                    Next

                    ' Want to get 5, 4, 12, 8, 5, 14, 37, 0, 0

                    Dim extract = numbers.Where(Function(x) x = 0).ToList()
                    Console.WriteLine("Extract")
                    For Each e In extract
                    Console.WriteLine(e)
                    Next

                    numbers.RemoveAll(Function(x) x = 0)
                    numbers.Sort()
                    numbers.AddRange(extract)
                    Console.WriteLine("After")
                    For Each e In numbers
                    Console.WriteLine(e)
                    Next

                    i.e., first take a copy of extract as a list before appending. New results Before 5 4 12 0 8 5 14 0 37 Extract 0 0 After 4 5 5 8 12 14 37 0 0 :-O Now tomorrow I should be able to fix my real code. Btw, if anyone has a more elegant solution to this feel free to offer it. I'm always willing to learn. :) Kevin

                    Kevin

                    M Offline
                    M Offline
                    Mohammad Dayyan
                    wrote on last edited by
                    #12

                    I've thought we shouldn't post programming questions here :rolleyes:

                    1 Reply Last reply
                    0
                    • L Lutoslaw

                      As Harold has suggested, it's better to just sort it using a simple comrasion formula defined as below:

                      (a, b) => a == 0 ? 1 : a - b

                      There is no ?: in VB, so

                      if (a == 0)
                      return 1;
                      else
                      return a - b;

                      Array.Sort(numbers, (a, b) => a == 0 ? 1 : a - b);

                      In the Real World:

                      Array.Sort(data, (a, b) => a.Equals(ZERO_DEFINITION) ? 1 : a.CompareTo(b);

                      EDIT: Looking at your code, It seems that a VB version would be somthing like his:

                      Array.Sort(numbers,
                      Function(a, b)
                      If a == 0 Then
                      return 1
                      else
                      return a - b);

                      Greetings - Gajatko Portable.NET is part of DotGNU, a project to build a complete Free Software replacement for .NET - a system that truly belongs to the developers.

                      A Offline
                      A Offline
                      Al Beback
                      wrote on last edited by
                      #13

                      gajatko wrote:

                      There is no ?: in VB, soif (a == 0) return 1;else return a - b;

                      VB has the IIf[^] function ("Inline If"), which is not short-circuited like the ternary operator, but for this simple case works just fine:

                      Array.Sort(number, Function(a, b) IIf(a = 0, 1, a - b))

                      ShamWow

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups