Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get "remainder" of query (all non-matched pairs) in LINQ and collect it in a list

Tags:

c#

linq

I am matching requests with responses based on requestId as follows:

    public void MatchCallPairsByRequestId()
    {
        // Finds request that have matching response based on RequestId and create callpair
       _callPairs = _requests.Where(req => !string.IsNullOrEmpty(req.RequestId))
                  .Join(_responses, 
                        req => req.RequestId,
                        resp => resp.RequestId,
                        (req, resp) => new CallPair(req, resp)).ToList();
    }

or in LINQ expression:

 _callPairs = (from req in _requests
                  join resp in _responses
                      on req.RequestId equals resp.RequestId
                      where !string.IsNullOrEmpty(req.RequestId)
                  select new CallPair(req, resp)).ToList();

Now I want to collect the requests and responses that are not matched by the function in a separate list called nonMatchedRequests and nonMatchedResponses. How do I use this query to collect the remainder in a separate list?

like image 968
Erwin Rooijakkers Avatar asked Mar 21 '23 06:03

Erwin Rooijakkers


2 Answers

I'm not sure if there is a way to do this in one call or perhaps even merge it with producing the list of pairs, but you can run a couple of follow up methods to determine the unmatched items:

var unmatchedRequests = _requests.Except(_callPairs.Select(cp => cp.Request));

var unmatchedResponses = _responses.Except(_callPairs.Select(cp => cp.Response));

The documentation for Enumerable.Join also talks of being able to use GroupJoin to perform an outer join, as detailed here, this will return unmatched requests, though I think it would miss unmatched responses.

I await with bated breath the answer demonstrating linq wizardry that does this more efficiently with one call.

like image 97
Adam Houldsworth Avatar answered Apr 26 '23 04:04

Adam Houldsworth


You probably can do this through .Except(), maybe also operating on distinct values

// Matching pairs
_callPairs = _requests.Where(req => !string.IsNullOrEmpty(req.RequestId))
    .Join(
        _responses, 
        req => req.RequestId,
        resp => resp.RequestId, 
        (req, resp) => new CallPair(req, resp)
    ).ToList();

// To use the .Distinct() part, you're going to need to implement IEqualityComparer twice
// Easy but maybe not strictly necessary, no matter what it would be a solid approach
var matchedRequests = _callPairs.Select(cp => cp.Request); //.Distinct(new RequestComparer());
var matchedResponses = _callPairs.Select(cp => cp.Response); //.Distinct(new ResponseComparer());

var nonMatchingRequests = _requests.Except(matchedRequests);
var nonMatchingResponses = _responses.Except(matchedResponses);
like image 44
Alex Avatar answered Apr 26 '23 06:04

Alex