Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add GroupJoin overload returning IGrouping<TOuter, TInner>#121999

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
Copilot wants to merge4 commits intomain
base:main
Choose a base branch
Loading
fromcopilot/add-tuple-return-overload

Conversation

Copy link
Contributor

CopilotAI commentedNov 26, 2025
edited
Loading

  • Understand the issue: Add a simplified GroupJoin overload returning IGrouping<TOuter, TInner>
  • Implement Enumerable.GroupJoin overload in System.Linq
  • Add reference assembly declaration for System.Linq
  • Implement Queryable.GroupJoin overload in System.Linq.Queryable
  • Add reference assembly declaration for System.Linq.Queryable
  • Add tests for System.Linq GroupJoin
  • Add tests for System.Linq.Queryable GroupJoin
  • Build and test the changes
  • Add IEqualityComparer parameter overloads (per reviewer feedback)
  • Add IAsyncEnumerable GroupJoin overloads (per reviewer feedback)
  • Run code review and codeql checker

Summary

This PR implements the approved API proposal from issue#120597 to add a simplified GroupJoin overload.

Approved API (Enumerable and Queryable):

namespaceSystem.Linq;publicstaticpartialclassEnumerable{publicstaticIEnumerable<IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisIEnumerable<TOuter>outer,IEnumerable<TInner>inner,Func<TOuter,TKey>outerKeySelector,Func<TInner,TKey>innerKeySelector,IEqualityComparer<TKey>?comparer=null);}publicstaticpartialclassQueryable{publicstaticIQueryable<IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisIQueryable<TOuter>outer,IEnumerable<TInner>inner,Expression<Func<TOuter,TKey>>outerKeySelector,Expression<Func<TInner,TKey>>innerKeySelector,IEqualityComparer<TKey>?comparer=null);}publicstaticclassAsyncEnumerable{publicstaticIAsyncEnumerable<IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisIAsyncEnumerable<TOuter>outer,IAsyncEnumerable<TInner>inner,Func<TOuter,CancellationToken,ValueTask<TKey>>outerKeySelector,Func<TInner,CancellationToken,ValueTask<TKey>>innerKeySelector,IEqualityComparer<TKey>?comparer=null);publicstaticIAsyncEnumerable<IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisIAsyncEnumerable<TOuter>outer,IAsyncEnumerable<TInner>inner,Func<TOuter,TKey>outerKeySelector,Func<TInner,TKey>innerKeySelector,IEqualityComparer<TKey>?comparer=null);}

This new overload removes the need to specify a result selector and returnsIGrouping<TOuter, TInner> where the Key is the outer element and the Elements are the correlated inner elements.

Original prompt

This section details on the original issue you should resolve

<issue_title>[API Proposal]: Add a GroupJoin overload returning a tuple of joined entries.</issue_title>
<issue_description># Motivation and Background
GroupJoin currently forces users to pass in a result selector which determines the shape of the return type. In most basic usages, users project out to an anonymous type (or a tuple) which wraps the returned TInner and TOuters - this proposal adds an additional overload of GroupJoin that removes to need to specify a result selector, and returns a tuple. We expect this will cover and simplify a large majority of the use-cases.

This proposal mirrors the simplified Zip overload, and also#120596 for Join.

API Proposal

namespaceSystem.Linq;publicstaticpartialclassEnumerable{publicstaticIEnumerable<IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisIEnumerable<TOuter>outer,IEnumerable<TInner>inner,Func<TOuter,TKey>outerKeySelector,Func<TInner,TKey>innerKeySelector,IEqualityComparer<TKey>?comparer=null);}publicstaticpartialclassQueryable{publicstaticIQueryable<IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisIQueryable<TOuter>outer,IEnumerable<TInner>inner,Expression<Func<TOuter,TKey>>outerKeySelector,Expression<Func<TInner,TKey>>innerKeySelector,IEqualityComparer<TKey>?comparer=null);}publicstaticclassAsyncEnumerable{publicstaticIAsyncEnumerable<IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisIAsyncEnumerable<TOuter>outer,IAsyncEnumerable<TInner>inner,Func<TOuter,CancellationToken,ValueTask<TKey>>outerKeySelector,Func<TInner,CancellationToken,ValueTask<TKey>>innerKeySelector,IEqualityComparer<TKey>?comparer=null);publicstaticIAsyncEnumerable<IGrouping<TOuter,TInner>>Join<TOuter,TInner,TKey>(thisIAsyncEnumerable<TOuter>outer,IAsyncEnumerable<TInner>inner,Func<TOuter,TKey>outerKeySelector,Func<TInner,TKey>innerKeySelector,IEqualityComparer<TKey>?comparer=null);}

Usage

vardepartments=newList<Department>{new(){Id=1,Name="HR"},new(){Id=2,Name="IT"},new(){Id=3,Name="Finance"}};varemployees=newList<Employee>{new(){Name="Alice",DepartmentId=1},new(){Name="Bob",DepartmentId=2},new(){Name="Charlie",DepartmentId=2},new(){Name="David",DepartmentId=3}};// New proposed simplified usage:foreach(var(emp,depts)inemployees.GroupJoin(departments, e=>e.DepartmentId, d=>d.Id)){Console.WriteLine($"Employee:{emp.Name}, departments:{string.Join(", ",depts.Select(i=>i.Name))}");}// Existing usage: explicit projection out to tuple requiredforeach(var(emp,depts)inemployees.GroupJoin(departments, e=>e.DepartmentId, d=>d.Id,(e,d)=>(e,d))){Console.WriteLine($"Employee:{emp.Name}, departments:{string.Join(", ",depts.Select(i=>i.Name))}");}classDepartment{publicintId{get;set;}publicstringName{get;set;}}classEmployee{publicstringName{get;set;}publicintDepartmentId{get;set;}}```</issue_description><agent_instructions>Please implement thenewpublicAPIsexactlyas describedin the API proposal section of the original post.</agent_instructions>##Comments onthe Issue(youare @copilotinthissection)<comments><comment_new><author>@eiriktsarpalis</author><body>Why wouldn'tthisreturn `(TOuter,IEnumerable<TInner>)`following thespirit ofyour otherproposalinhttps://github.com/dotnet/runtime/issues/120596?</body></comment_new><comment_new><author>@roji</author><body>I think it's problematicfor the returned IGrouping to have `(TKey,TOuter)`as its key:* For one thing,Joinreturns only TOuter and TInner(no key);thesame question has been[raised](https://github.com/dotnet/runtime/issues/120596#issuecomment-3389882707) for the proposed simplified Join syntax (should it also return the keys?), and that proposal doesn't return the key for (I believe) reasons which apply here as well.*Justlike for Join, the current result selectorsfor the existing GroupJoin(andJoin) acceptonly theouters andinners, and notthe key.If onereally wantsthe keys,one canalways referencethem againon theouter/inner insidethe selector;so there's noreason toimpose theextra complexityon everyone, andforthe fewwho doneed thekey,they cangetit(atleast for caseswhere the key selector isn't a complex expression that's potentially expensive to evaluate).*Having theIGrouping haveits TKeybe atuple ofthe GroupJoin's TKey andTOuteris,well,quite confusing andunwieldy,asTKey meanstwo completelydifferent things(it's thecorrelation...</details>-Fixes dotnet/runtime#120587<!-- START COPILOT CODING AGENT TIPS-->---💬 We'd love your input! Share your thoughts on Copilot coding agentin our[2 minutesurvey](https://gh.io/copilot-coding-agent-survey).

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
CopilotAI changed the title[WIP] Add GroupJoin overload returning a tuple of joined entriesAdd GroupJoin overload returning IGrouping<TOuter, TInner>Nov 26, 2025
Copilot finished work on behalf ofeiriktsarpalisNovember 26, 2025 20:17
publicstaticSystem.Linq.IQueryable<TResult>GroupBy<TSource,TKey,TResult>(thisSystem.Linq.IQueryable<TSource>source,System.Linq.Expressions.Expression<System.Func<TSource,TKey>>keySelector,System.Linq.Expressions.Expression<System.Func<TKey,System.Collections.Generic.IEnumerable<TSource>,TResult>>resultSelector,System.Collections.Generic.IEqualityComparer<TKey>?comparer){thrownull;}
publicstaticSystem.Linq.IQueryable<TResult>GroupBy<TSource,TKey,TElement,TResult>(thisSystem.Linq.IQueryable<TSource>source,System.Linq.Expressions.Expression<System.Func<TSource,TKey>>keySelector,System.Linq.Expressions.Expression<System.Func<TSource,TElement>>elementSelector,System.Linq.Expressions.Expression<System.Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult>>resultSelector){thrownull;}
publicstaticSystem.Linq.IQueryable<TResult>GroupBy<TSource,TKey,TElement,TResult>(thisSystem.Linq.IQueryable<TSource>source,System.Linq.Expressions.Expression<System.Func<TSource,TKey>>keySelector,System.Linq.Expressions.Expression<System.Func<TSource,TElement>>elementSelector,System.Linq.Expressions.Expression<System.Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult>>resultSelector,System.Collections.Generic.IEqualityComparer<TKey>?comparer){thrownull;}
publicstaticSystem.Linq.IQueryable<System.Linq.IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisSystem.Linq.IQueryable<TOuter>outer,System.Collections.Generic.IEnumerable<TInner>inner,System.Linq.Expressions.Expression<System.Func<TOuter,TKey>>outerKeySelector,System.Linq.Expressions.Expression<System.Func<TInner,TKey>>innerKeySelector){thrownull;}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@copilot you missed the optional IEqualityComparer parameter here. Please add it.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Added theIEqualityComparer<TKey>? comparer parameter overloads for bothEnumerable.GroupJoin andQueryable.GroupJoin in commitd8cc66d.

publicstaticSystem.Collections.Generic.IEnumerable<TResult>GroupBy<TSource,TKey,TResult>(thisSystem.Collections.Generic.IEnumerable<TSource>source,System.Func<TSource,TKey>keySelector,System.Func<TKey,System.Collections.Generic.IEnumerable<TSource>,TResult>resultSelector,System.Collections.Generic.IEqualityComparer<TKey>?comparer){thrownull;}
publicstaticSystem.Collections.Generic.IEnumerable<TResult>GroupBy<TSource,TKey,TElement,TResult>(thisSystem.Collections.Generic.IEnumerable<TSource>source,System.Func<TSource,TKey>keySelector,System.Func<TSource,TElement>elementSelector,System.Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult>resultSelector){thrownull;}
publicstaticSystem.Collections.Generic.IEnumerable<TResult>GroupBy<TSource,TKey,TElement,TResult>(thisSystem.Collections.Generic.IEnumerable<TSource>source,System.Func<TSource,TKey>keySelector,System.Func<TSource,TElement>elementSelector,System.Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult>resultSelector,System.Collections.Generic.IEqualityComparer<TKey>?comparer){thrownull;}
publicstaticSystem.Collections.Generic.IEnumerable<System.Linq.IGrouping<TOuter,TInner>>GroupJoin<TOuter,TInner,TKey>(thisSystem.Collections.Generic.IEnumerable<TOuter>outer,System.Collections.Generic.IEnumerable<TInner>inner,System.Func<TOuter,TKey>outerKeySelector,System.Func<TInner,TKey>innerKeySelector){thrownull;}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@copilot likewise, this is missing an IEqualityComparer parameter.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The IEqualityComparer parameter was already added in commitd8cc66d - see line 85 in the ref file.

Copy link
Member

@eiriktsarpaliseiriktsarpalis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@copilot you forgot to add the corresponding IAsyncEnumerable overloads in the System.Linq.AsyncEnumerable project.

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Copy link
ContributorAuthor

@copilot you forgot to add the corresponding IAsyncEnumerable overloads in the System.Linq.AsyncEnumerable project.

Added the IAsyncEnumerable GroupJoin overloads (both sync and async key selector variants) in commit05ca46a.

Copilot finished work on behalf ofeiriktsarpalisNovember 27, 2025 14:11
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@rojirojiAwaiting requested review from roji

@eiriktsarpaliseiriktsarpalisAwaiting requested review from eiriktsarpalis

At least 1 approving review is required to merge this pull request.

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@eiriktsarpalis

[8]ページ先頭

©2009-2025 Movatter.jp