简体   繁体   中英

LINQ check list of objects contain a value AND not some other values

Let's say I have this list of objects:

var shipments = new List<ShipmentTracking>() {
    new ShipmentTracking() {
        trackingNumber = "32021001000", trackings = new List<Tracking>() {
            new Tracking() { trackingCode = "EBC", trackingPoint = "Entered", trackingDateTime = DateTime.Now, trackingMemo = "" }
        }
    },
    new ShipmentTracking() {
        trackingNumber = "32021001001", trackings = new List<Tracking>() {
            new Tracking() { trackingCode = "EBC", trackingPoint = "Entered", trackingDateTime = DateTime.Now.AddDays(1), trackingMemo = "" },
            new Tracking() { trackingCode = "AWB", trackingPoint = "Registered", trackingDateTime = DateTime.Now.AddDays(1), trackingMemo = "" }
        }
    },
    new ShipmentTracking() {
        trackingNumber = "32021001002", trackings = new List<Tracking>() {
            new Tracking() { trackingCode = "EBC", trackingPoint = "Entered", trackingDateTime = DateTime.Now.AddDays(2), trackingMemo = "" },
            new Tracking() { trackingCode = "AWB", trackingPoint = "Registered", trackingDateTime = DateTime.Now.AddDays(2), trackingMemo = "" },
            new Tracking() { trackingCode = "DSP", trackingPoint = "Shipped", trackingDateTime = DateTime.Now.AddDays(2), trackingMemo = "" }
        }
    },
    new ShipmentTracking() {
        trackingNumber = "32021001003", trackings = new List<Tracking>() {
            new Tracking() { trackingCode = "EBC", trackingPoint = "Entered", trackingDateTime = DateTime.Now.AddDays(3), trackingMemo = "" },
            new Tracking() { trackingCode = "AWB", trackingPoint = "Registered", trackingDateTime = DateTime.Now.AddDays(3), trackingMemo = "" },
            new Tracking() { trackingCode = "DSP", trackingPoint = "Shipped", trackingDateTime = DateTime.Now.AddDays(3), trackingMemo = "" },
            new Tracking() { trackingCode = "CCI", trackingPoint = "Cleared", trackingDateTime = DateTime.Now.AddDays(3), trackingMemo = "" }
        }
    },
    new ShipmentTracking() {
        trackingNumber = "32021001003", trackings = new List<Tracking>() {
            new Tracking() { trackingCode = "EBC", trackingPoint = "Entered", trackingDateTime = DateTime.Now.AddDays(4), trackingMemo = "" },
            new Tracking() { trackingCode = "AWB", trackingPoint = "Registered", trackingDateTime = DateTime.Now.AddDays(4), trackingMemo = "" },
            new Tracking() { trackingCode = "DSP", trackingPoint = "Shipped", trackingDateTime = DateTime.Now.AddDays(4), trackingMemo = "" },
            new Tracking() { trackingCode = "CCI", trackingPoint = "Cleared", trackingDateTime = DateTime.Now.AddDays(4), trackingMemo = "" },
            new Tracking() { trackingCode = "POD", trackingPoint = "Delivered", trackingDateTime = DateTime.Now.AddDays(4), trackingMemo = "" }
        }
    }
};

I need the query that would check and return only shipment tracking(s) that has trackingCode == "DSP" but not shipment tracking with "CCI" or "POD" trackingCode, so in this example shipment tracking with tracking number 32021001002 would be the one.

I have tried this but doesn't seem to work:

foreach (var shipment in shipments)
{
    var foo = shipment.trackings.FirstOrDefault((t => t.trackingCode == "DSP" && t.trackingCode != "CCI"));

    if (shipment.trackings.Contains(foo))
    {
        
    }
}

Any help or pointer is appreciated. Thanks!

You want shipments where for the list of trackings: there IS any tracking code "DSP" and there IS NOT any tracking code "CCI"

shipments.Where(s => 
  s.Trackings.Any(t => t.TrackingCode == "DSP") && 
  !s.Trackings.Any(t => t.TrackingCode == "CCI")
);

You could also write this as:

shipments.Where(s => 
  s.Trackings.Any(t => t.TrackingCode == "DSP") && 
  s.Trackings.All(t => t.TrackingCode != "CCI")
);

ie shipments where there is any tracking code that is DSP, and all the tracking codes are not CCI"

Use whichever makes more sense to you

has trackingCode == "DSP" but not shipment tracking with "CCI" or "POD"

I would fill two collections to simplify it and to make it easy to be changed:

var validCodes = new List<string>{"DSP"};
var invalidCodes = new List<string>{"CCI", "POD"};

If you want to ignore the case, so treat dsp and DSP same you need StringComparer.OrdinalIgnoreCase , otherwise StringComparer.Ordinal .

The query is pretty simple:

List<ShipmentTracking> trackingList = shipments
    .Where(st => st.trackings.Any(t => validCodes.Contains(t.trackingCode, StringComparer.OrdinalIgnoreCase)))
    .Where(st => !st.trackings.Any(t => invalidCodes.Contains(t.trackingCode, StringComparer.OrdinalIgnoreCase)))
    .ToList();

Instead of two Where you can also use one with && . I prefer two because it's easier to read.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM