Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MaxBy inconsistent results with tie-breaks #61317

Closed
Tracked by #64601
martingbrown opened this issue Nov 8, 2021 · 3 comments · Fixed by #61364
Closed
Tracked by #64601

MaxBy inconsistent results with tie-breaks #61317

martingbrown opened this issue Nov 8, 2021 · 3 comments · Fixed by #61364
Assignees
Labels
area-System.Linq enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@martingbrown
Copy link

martingbrown commented Nov 8, 2021

Description

When all the items in the list have a null key MaxBy returns the last item in the list. When they all have a key value but the values are the same the first item is returned. To me that seems inconsistent.

Reproduction Steps

using System;
using System.Linq;
					
public class Program
{
	public static void Main()
	{
		var l1 = new Tuple<string, string>[]
		{
			new Tuple<string, string>("", "A"),
			new Tuple<string, string>("", "B")
		};
		
		var l2 = new Tuple<string, string>[]
		{
			new Tuple<string, string>(null, "A"),
			new Tuple<string, string>(null, "B")
		};
		
		var r1 = l1.MaxBy(t => t.Item1);
		var r2 = l2.MaxBy(t => t.Item1);
		
		Console.WriteLine(r1.Item2);
		Console.WriteLine(r2.Item2);
	}
}

Expected behavior

The Reproduction code should print AA or BB.

Actual behavior

The reproduction code prints AB

Regression?

I think this is new in .Net 6.

Known Workarounds

Don't use lists with all the keys the same.

Configuration

I got this in .Net 6 RC2. I don't think the OS or CPU are going to make any difference.

Other information

No response

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Nov 8, 2021
@ghost
Copy link

ghost commented Nov 8, 2021

Tagging subscribers to this area: @dotnet/area-system-linq
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

When all the items in the list have a null key MaxBy returns the last item in the list. When they all have a key value but the values are the same the first item is returned. To me that seems inconsistent.

Reproduction Steps

using System;
using System.Linq;
					
public class Program
{
	public static void Main()
	{
		var l1 = new Tuple<string, string>[]
		{
			new Tuple<string, string>("", "A"),
			new Tuple<string, string>("", "B")
		};
		
		var l2 = new Tuple<string, string>[]
		{
			new Tuple<string, string>(null, "A"),
			new Tuple<string, string>(null, "B")
		};
		
		var r1 = l1.MaxBy(t => t.Item1);
		var r2 = l2.MaxBy(t => t.Item1);
		
		Console.WriteLine(r1.Item2);
		Console.WriteLine(r2.Item2);
	}
}

Expected behavior

The Reproduction code should print AA or BB.

Actual behavior

The reproduction code prints AB

Regression?

I think this is new in .Net 6.

Known Workarounds

Don't use lists with all the keys the same.

Configuration

I got this in .Net 6 RC2. I don't think the OS or CPU are going to make any difference.

Other information

No response

Author: martingbrown
Assignees: -
Labels:

area-System.Linq, untriaged

Milestone: -

@eiriktsarpalis
Copy link
Member

Hi @martingbrown, this is by-design behavior intended to preserve compatibility with null element handling in the existing Max and Min methods: null keys have special meaning and are always treated as bottom elements regardless of what comparer is being passed by the user. As a side-effect of that behavior is that the methods will always return the last element if every key in a sequence is null.

That being said, whether the Max method is returning the first or last null has no observable difference on the user's output. This is not the case with MaxBy so I can see how this can take users by surprise. Note however that the Max* method documentation provides no guarantees on what element will be returned in the event of a tie, so I believe this behavior to be well-within specification. We can consider refining the behavior in a future release.

As a workaround -assuming order of selection is important- I would recommend avoiding null keys since they are always treated bottom elements regardless of comparer. In your case this would mean writing:

var r2 = l2.MaxBy(t => t.Item1 ?? "");

@eiriktsarpalis eiriktsarpalis added enhancement Product code improvement that does NOT require public API changes/additions and removed untriaged New issue has not been triaged by the area owner labels Nov 9, 2021
@eiriktsarpalis eiriktsarpalis added this to the 7.0.0 milestone Nov 9, 2021
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Nov 9, 2021
@eiriktsarpalis eiriktsarpalis self-assigned this Nov 9, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Nov 9, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Dec 9, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Linq enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants