解决 C# Collection 不匹配问题

Solving C# Collection Mismatch

我有两个 collections - 一个包含产品标题 - 第二个包含运输信息。我检索此信息的方式保证每个 collection 中的每个位置都在一起,即 collection 1 中的位置 [0] 与 collection 2 中的位置 [0] 一起。但是,每当同一个客户订购了两个或更多不同的项目,这不再是真的 - 它在我的 collection 中造成了不匹配。

在下面的示例中,同一位客户 dave lain,订购了两种不同的商品,在我的 'b' collection 中灌装了两种不同的药水,但他只会填补我的 'a' collection 中的一个位置。当我去迭代两个 collections - 它们不会正确匹配并且错误的人将被记入错误的顺序。

XML 产品标题文件 COLLECTION 'b':

<ListOrderItems>
    <OrderItem>
        <Title>ItemfromCustomer1</Title>
    </OrderItem>
    <OrderItem>
        <Title>ItemfromCustomer1</Title>
    </OrderItem>
    <OrderItem>
        <Title>ItemfromCustomer2</Title>
    </OrderItem>
    <OrderItem>
        <Title>ItemfromCustomer3</Title>
    </OrderItem>
</ListOrderItems>

送货信息COLLECTION'a':

<ListOrdersResult>
    <Orders>
        <Order>
            <Name>dave lain</Name>
            <AddressLine1>630232 JEKASS RD</AddressLine1>
            <City>PARIS</City>
            <StateOrRegion>TX</StateOrRegion>
            <PostalCode>3281-5454</PostalCode>
            <CountryCode>US</CountryCode>
            <Phone>407745186</Phone
        </Order>
        <Order>
            <Name>JOE SCHMO</Name>
            <AddressLine1>635232 CHERRY RD</AddressLine1>
            <City>PARIS</City>
            <StateOrRegion>TX</StateOrRegion>
            <PostalCode>876458</PostalCode>
            <CountryCode>US</CountryCode>
            <Phone>5425546574</Phone                  
        </Order>
        <Order>
            <Name>RAY CHARLES</Name>
            <AddressLine1>6384 RED DR</AddressLine1>
            <City>MIAMI</City>
            <StateOrRegion>FL</StateOrRegion>
            <PostalCode>78457</PostalCode>
            <CountryCode>US</CountryCode>
            <Phone>5478975487</Phone                  
        </Order>
    </Orders>
</ListOrdersResult>

我的 foreach 循环使用 zip 方法并行遍历两个 collection,但是当它达到双顺序时,一切都停止了。

foreach (var address in addresses.Zip(products, (a, b) => new {a, b}))
{    
    if (string.Equals(address.b.Title, denatAlcohol, StringComparison.CurrentCultureIgnoreCase))
    {
        //execute code if product title is a match
    }

    if (string.Equals(address.b.Title, sodiumChlorate, StringComparison.CurrentCultureIgnoreCase))
    {
        //execute code if product title is a match
    }
    if (string.Equals(address.b.Title, semiNitric, StringComparison.CurrentCultureIgnoreCase))
    {
        //execute code if product title is a match
    }
    if (string.Equals(address.b.Title, leserNitric, StringComparison.CurrentCultureIgnoreCase))
    {
        //execute code if product title is a match
    }
    if (stringArray.Any(address.b.Title.Contains))
    {
        //execute code if product title is a match                        
    }
    if (address.b.Title.Equals(isopropyl))
    {
        //execute code if product title is a match           
    }
    if (address.b.Title.Equals(hazNitric))
    {
        //execute code if product title is matchg
    }     
    if (address.a.AddressLine2 != null)
    {
        //execute code with info in collection a for shipping address info
    }
    else
    {
        //execute code with info in collection a for shipping address info
    }
}

由于您的示例未提供强制 Order 元素和 OrderItem 元素之间关系的某种键的任何信息,因此我假设 Title 是语义键。

Enumerable.Zip方法

combines elements until it reaches the end of one of the sequences.

就像你的例子一样,

[...] if one sequence has three elements and the other one has four, the result sequence has only three elements.

所以,实际发生的是第二步的 Zip 方法将 OrderItem ItemFromCustomer1 与第二个 Order 元素相关联序列 JOE SCHMO.

您需要做的是对 OrderItem 进行分组,然后像这样展平集合:

addresses.Descendants("Order")
    .Zip(orders.Descendants("OrderItem")
                .GroupBy(e=>e.Element("Title").Value), 
            (a, o) => new{a,o})
    .SelectMany(tuple => tuple.o.Select(x => new{tuple.a, o = x}));

这是 LINQPad 中的结果:

但是,请记住 这不是正确的做法! 您需要定义 Order 元素与由某种外键强制执行的 OrderItem 元素之间的关系,以便您可以对这些集合执行适当的 Join 而无需担心排序问题;像这样:

addresses.Descendants("Order")
    .Join(orders.Descendants("OrderItem"),
        order => order.Element("Id").Value,
        orderItem => orderItem.Element("OrderId").Value,
        (order, orderItem) => new{order, orderItem});