-1

I want to use the .net LINQ GroupBy method to group data points by x and y co-ordinates. I wasn't sure how to combine these x and y co-ordinates into a single entity to group on.

First, I tried combining them into an array of two elements. This didn't work, I believe, because even when they contain the same two numbers, two arrays aren't the same array.

Second, I tried converting them to strings and concatenating them like so: groupedData = data.GroupBy(a => a.x.ToString() + "," + a.y.ToString()); This appears to work, but it is obviously inefficient as the string conversion should be unnecessary.

What is a better way to do what I want to accomplish, preferably without using anonymous types?

Note: I must do this with .net 3.5.

Edit: This question is, indeed, almost exactly the same as the one it is marked as a duplicate of. However, the original question doesn't ask how to do this without using anonymous types and doesn't have the constraint that it has to work in .net 3.5.

Vivian River
  • 31,198
  • 62
  • 198
  • 313
  • Why you want to avoid anonymous types ? – user2711965 Mar 10 '15 at 15:52
  • I would like to be able to return the result of `GroupBy` from the function. – Vivian River Mar 10 '15 at 15:53
  • 1
    What do you want to return as a result ? `List` or do you have a class for coordinates ? – Habib Mar 10 '15 at 15:54
  • 1
    What should be the return value ? – user2711965 Mar 10 '15 at 15:54
  • I suppose that I could create a class just to keep the two coordinates, but this seems a bit excessive, to create a whole new class just to be able to use it in a one-line lambda function. – Vivian River Mar 10 '15 at 15:55
  • `Tuple` would qualify as a great answer. However, I'm stuck using .net 3.5 for this project, which doesn't contain that type. It is unfortunate and completely beyond my control. – Vivian River Mar 10 '15 at 15:57
  • @DanielAllenLangdon, depending on your application, you can use existing `Point` class, or create a new class, or use `Tuple` as pointed out by `RobH` – Habib Mar 10 '15 at 15:58
  • Point class doesn't work because it only works with integers, and my numbers are floating-point. It is becoming apparent to me that I should have seen that there must be a concrete class with an equality comparison to do what I want. – Vivian River Mar 10 '15 at 16:04
  • @DanielAllenLangdon, there is a [PointF](https://msdn.microsoft.com/en-us/library/system.drawing.pointf%28v=vs.110%29.aspx) for floating points' point as well. – Habib Mar 10 '15 at 16:08

2 Answers2

0

You can specify multiple fields in a group by like:

var query = data.GroupBy(a=> new { X = a.x, Y = a.y)};

There is no need to do grouping based on string concatenation.

Habib
  • 219,104
  • 29
  • 407
  • 436
  • Thanks for the answer. Can you explain why it is that using an anonymous type works with `GroupBy`, but an array does not? MSDN says, "Anonymous types are class types that derive directly from object, and that cannot be cast to any type except object. The compiler provides a name for each anonymous type, although your application cannot access it. From the perspective of the common language runtime, an anonymous type is no different from any other reference type." Based on the above, I would think that no two of these would be the same any more than two arrays would be the same. – Vivian River Mar 10 '15 at 16:07
  • BTW, the quote above is from this page: https://msdn.microsoft.com/en-us/library/bb397696.aspx – Vivian River Mar 10 '15 at 16:07
  • 1
    @DanielAllenLangdon, I am not sure how you are specifying an array in group by , but probably since in case of Array's, a reference comparison would be performed, whereas in case of [anonymous type comparison is done based on fields](http://stackoverflow.com/questions/12123512/why-anonymous-types-equals-implementation-compares-fields) – Habib Mar 10 '15 at 16:09
  • I wonder why they didn't just make anonymous types value types instead of reference types then. It seems they behave like a hybrid of the two. – Vivian River Mar 10 '15 at 16:12
  • 1
    @DanielAllenLangdon, they are immutable and have an Equality comparison, but value types are usually of specific size, In case of anonymous type, it can be of any size. Similar to how strings are. – Habib Mar 10 '15 at 16:15
  • @Habib anonymous types could most certainly be value types. anonymous types in general are not of a fixed size, but in reality every usage of anonymous types involves the creation of a new named type, that name is just never accessible in the C# source code. Each actual concrete type will have a fixed size. They could absolutely be value types instead. Whether or not it would be a good idea is subjective, but it's unquestionably possible. – Servy Mar 10 '15 at 17:54
  • @Servy, so following that ^^ , Can string *be* value types as well ? – Habib Mar 10 '15 at 18:01
  • @Habib Absolutely. A string is just a wrapper around a `char[]`. That reference to the array could itself be either a value type or a reference type. It is the `char[]` that cannot be a value type; it must be a reference type. – Servy Mar 10 '15 at 18:03
  • @Habib The type itself couldn't contain all of the information for the string in the instance itself if it is a `struct`. Of course, if it *isn't doing that* (say, by only holding a reference to the data that is elsewhere) then it no longer applies. – Servy Mar 10 '15 at 18:13
0

Were I using .net 4.0 or above, I could use the generic Tuple class to represent a pair of numbers. MSDN clearly indicates (https://msdn.microsoft.com/en-us/library/dd270346%28v=vs.110%29.aspx) that Tuple implements an equality comparer that compares the values, not a simple reference comparison, so that should work.

In .net 3.5, it looks like there is really no generic way of doing this without resorting to anonymous types. I simply undertook to change the design of my code so that anonymous types won't cause a problem.

Otherwise, it looks necessary to either use some type in the .net framework, such as System.Drawing.Point or System.Drawing.PointF, or create a class specifically for this.

Servy
  • 202,030
  • 26
  • 332
  • 449
Vivian River
  • 31,198
  • 62
  • 198
  • 313