0

I need to compare nested json collections against one another using C# linq. Here is an example of the json collections and the classes that they get bound to.

=== collection 1 ===

[
    {
        "contractId": "100-200-A",
        "invoices": [
            {
                invoiceNumber: 987654
            },
            {
                invoiceNumber: 555999
            }           
        ]
    },
    {
        "contractId": "300-777-Z",
        "invoices": [
            {
                invoiceNumber: 12345
            },
            {
                invoiceNumber: 100025
            }           
        ]
    }
]

public class Contract
{
    public string ContractId { get; set; }
    public IList<Invoice> Invoices { get; set; }

    public class Invoice 
    {
        public int InvoiceNumber { get; set; }
    }
}

=== collection 2 ===

[
    {
        "paymentDate": "01/01/2000",
        "contracts":[
            {
                "contractId": "100-200-A",
                "invoices": [
                    {
                        invoiceNumber: 987654
                    },
                    {
                        invoiceNumber: 555999
                    },
                    {
                        invoiceNumber: 444333
                    },
                    {
                        invoiceNumber: 111000
                    }
                ]
            },
            {
                "contractId": "300-777-Z",
                "invoices": [
                    {
                        invoiceNumber: 12345
                    },
                    {
                        invoiceNumber: 100025
                    },
                    {
                        invoiceNumber: 888666
                    },
                    {
                        invoiceNumber: 222999
                    }                   
                ]
            }
        ]
    }
]

public class PaymentRequest
{
    public DateTime PaymentDate { get; set; }
    public IList<ContractList> Contracts { get; set; }
    public class ContractList
    {       
        public string ContractId { get; set; }  
        public IList<InvoiceList> Invoices { get; set; }
    }

    public class InvoiceList
    {          
        public int InvoiceNumber { get; set; }
    }
}

Assuming that the contractId's are the same in both collections, I can create a linq join between the two, I am having a hard time trying to find the extra invoices that are in the 2nd collection set. Essentially, I need to get back a list of all the invoice numbers for that specific contractId that are in the 2nd collection that are not present in the 1st collection.

2
  • Why do you have two different classes for the same thing? Invoice vs InvoiceList and Contract vs ContractList? Commented Feb 20, 2016 at 17:04
  • valid question. dealing with existing implementation and a lot of class properties (from both) were left off for brevity Commented Feb 20, 2016 at 17:17

3 Answers 3

1

What you are asking for is called antijoin. The corresponding LINQ construct is group join with check for empty inner group. See How can I use LINQ to avoid calling Contains() inside a Where() clause?

Applying it to your use case (assuming the same data structure as in your previous questions) could be something like this:

var query =
    from request in (
        from contract in paymentRequest.Contracts
        from invoice in contract.Invoices
        select new { contract, invoice }
    )
    join valid in (
         from contract in validContracts
         from invoice in contract.InvoiceList
         select new { contract, invoice }
    )
    on new { request.contract.ContractId, request.invoice.InvoiceNumber }
    equals new { valid.contract.ContractId, valid.invoice.InvoiceNumber }
    into validRequests
    where !validRequests.Any()
    select request;
Sign up to request clarification or add additional context in comments.

Comments

0

Try this out:

foreach (var contract in payment.Contracts)
{
    var otherContract = contracts.First(c => c.ContractId == contract.ContractId);
    var missingInvoiceNumbers = contract.Invoices.Where(i => !otherContract.Invoices.Any(inv => inv.InvoiceNumber == i.InvoiceNumber))
                                                 .Select(i => i.InvoiceNumber);
    // do something with the results, like adding them to a dictionary <contract.ContractId, missingInvoiceNumbers>
}

It's not pure LINQ, but better have it readable for future.

Comments

0

find the extra invoices that are in the 2nd collection set

Let's call list1 and list2 to the contacts lists you need to compare. The linq should be like this:

list1.Join(list2, x=>x.ContactId, x=>x.ContactId, 
    (c1,c2)=>new {
        c2.ContactId,
        MissingInv=c2.Invoices.Where(x=>c1.Invoices.All(y=>x!=y))
    });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.