Как объединить два плоских списка в один вложенный объект - PullRequest
1 голос
/ 30 мая 2020

У меня есть три списка, которые содержат три одинаковых свойства в каждой коллекции. Я хочу объединить результат в одну коллекцию. Структура классов Ex выглядит следующим образом:

public class Order
{
    public int ProductId { get; set; }
    public int CustomerId { get; set; }
    public int OrderId { get; set; }

    // Few other Properties of OrderDetail
}
public class PaymentDetail
{
    public int ProductId { get; set; }
    public int CustomerId { get; set; }
    public int OrderId { get; set; }
    // Few other Properties form PaymentDetail
}

public class CouponUsageDetail
{
    public int ProductId { get; set; }
    public int CustomerId { get; set; }
    public int OrderId { get; set; }
    // Few other Properties form CouponUsageDetail
}

Этот тип вывода поступает из одной службы API, где каждый класс находится в форме объекта списка (формат JSON), и нам нужно выполнить некоторые операции с этим. Все три свойства (ProductId, CustomerId, OrderId) содержат одинаковые значения в каждой коллекции, что означает, что все три свойства повторяются в каждой коллекции. Нам нужно выполнить поиск в этих коллекциях. Итак, обычным способом мы можем сделать с ним следующее: запустить foreach из списка заказов и отфильтровать все три совпадающих свойства PaymentDetail и CouponUsageDetail. Но при увеличении размера данных это будет дороже с точки зрения производительности. Так что подумайте о создании структуры вложенности заранее и избегайте поиска. Если мы сделаем вывод вложенным, как показано ниже, это поможет избежать поиска в PaymentDetail и CouponUsageDetail. Пример - мы получаем JOSN в формате ниже

{"Orders":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}],"PaymentDetails":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}],"CouponUsageDetails":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}]}

, и с этим выводом мы хотим сформировать объект как

public class OrderDetails
{
    public int ProductId { get; set; }
    public int CustomerId { get; set; }
    public int OrderId { get; set; }
    // Few other Properties of OrderDetail

    List<PaymentDetail> PaymentDetail { get; set; }
    List<CouponUsageDetail> CouponUsageDetail { get; set; }
}

Можете ли вы указать, что было бы наилучшим оптимальным использованием linq где мы можем объединить все эти три совпадающих свойства и улучшить только одну вложенную структуру? Спасибо! Примечание : Я знаю, что эту структуру необходимо нормализовать, но, пожалуйста, не обращайте внимания на приведенное здесь правило нормализации, так как это не находится под нашим контролем.

Ответы [ 4 ]

1 голос
/ 31 мая 2020

То, что вы описываете, звучит как два стандартных многоклавишных LINQ групповое объединение . Они довольно эффективны (реализация LINQ to Objects использует подготовленные быстрые поиски на основе ha sh), поэтому дальнейшая оптимизация не требуется:

var orderDetails = (
    from o in data.Orders
    join p in data.PaymentDetails
        on new { o.ProductId, o.CustomerId, o.OrderId }
        equals new { p.ProductId, p.CustomerId, p.OrderId }
        into orderPaymentDetails
    join c in data.CouponUsageDetails
        on new { o.ProductId, o.CustomerId, o.OrderId }
        equals new { c.ProductId, c.CustomerId, c.OrderId }
        into orderCouponUsageDetails
    select new OrderDetails
    {
        ProductId = o.ProductId,
        CustomerId = o.CustomerId,
        OrderId = o.OrderId,
        // Few other Properties of OrderDetail
        PaymentDetail = orderPaymentDetails.ToList(),
        CouponUsageDetail = orderCouponUsageDetails.ToList(),
    })
    .ToList();
1 голос
/ 31 мая 2020

Похоже, здесь есть несколько вопросов, я постараюсь их проработать:

Модели данных и десериализация

Что касается создания единой структуры из вашего ответа API , Я бы рекомендовал использовать библиотеки Newtonsoft. Json, доступные на NuGet Json. NET. Они позволят вам десериализовать ответ от вашего API в один объект, который, учитывая предоставленный вами образец, должен содержать коллекцию каждой из ваших моделей, Order, PaymentDetail, CouponUsageDetail:

public class APIResponceContainer
{
    [JsonProperty("Orders")]
    public List<Order> Orders { get; set; }
    [JsonProperty("PaymentDetails")]
    public List<PaymentDetail> PaymentDetails { get; set; }
    [JsonProperty("CouponUsageDetails")]
    public List<CouponUsageDetail> CouponUsageDetails { get; set; }

    public APIResponceContainer()
    {
        Orders = new List<Order>();
        PaymentDetails = new List<PaymentDetail>();
        CouponUsageDetails = new List<CouponUsageDetail>();
    }
}

Не забывайте добавлять обязательные атрибуты к каждой из ваших моделей следующим образом:

public class Order
{
    [JsonProperty("ProductId")]
    public int ProductId { get; set; }
    [JsonProperty("CustomerId")]
    public int CustomerId { get; set; }
    [JsonProperty("OrderId")]
    public int OrderId { get; set; }
}

Затем десериализация происходит из вашей JSON строки, как таковой:

StringReader stringReader = new StringReader(myJSONString);
JsonSerializer js = JsonSerializer.Create();
APIResponceContainer APIResponce = (APIResponceContainer)js.Deserialize(stringReader, typeof(APIResponceContainer));

Запросы

Как обсуждалось в комментариях, ваши данные, к сожалению, очень нуждаются в нормализации. Однако я сделал вывод, что вы хотели бы создать плоскую структуру, сохраняя «несколько других свойств» и «ключевые свойства» для комбинации Order, PaymentDetail и CouponUsageDetail. Вы можете использовать Linq для этого, что важно, я бы рекомендовал вам выбрать себе «Первичный ключ». Другими словами, одно свойство, которое может независимо ie всех остальных вместе. В приведенном ниже примере я выбрал OrderID, поскольку он должен быть уникальным (?):

var flatSequence =
            from order in APIResponce.Orders
            join coupon in APIResponce.CouponUsageDetails on order.OrderId equals coupon.OrderId
            join payment in APIResponce.PaymentDetails on order.OrderId equals payment.OrderId
            select new
            {
                // Here extract all the properties you care about
                OrderID = order.OrderId,
                Customer = order.CustomerId,
                Product = order.ProductId,
                // All the "other Properties" ?
                BankDetail = payment.PaymentOnlyProperty
            };

Здесь я извлек в var, однако, если вы знаете окончательную плоскую структуру, которую хотите, конечно, определите свой класс, чтобы получить результат.

Прокомментируйте, если есть какие-либо вопросы.

0 голосов
/ 30 мая 2020

Вы можете использовать наследование.

public class ResultCollection : Collection1
{
    List<Collection2> Collection2s { get; set; }
    List<Collection3> Collection3s { get; set; }
}

, а затем

var result = new ResultCollection {
    PropId1 = Collection1.PropId1,
    PropId2 = Collection1.PropId2,
    ...
    Collection2s = Collection2,
    Collection3s = Collection3
}

Здесь может оказаться полезным автоматическое преобразование.
https://docs.automapper.org/en/stable/

0 голосов
/ 30 мая 2020

У меня есть решение, но я не уверен, подходит ли оно вам. Это зависит от формата данных, который у вас есть в начале.

решение:

class Program
{
    static void Main(string[] args)
    {
        var collection1 = new Collection1() { PropId1 = 1, PropId2 = 2, PropId3 = 3 };

        var list2 = new List<Collection2>()
        {
            new Collection2
            {
                PropId1 = 11,
                PropId2 = 22,
                PropId3 = 33
            },
            new Collection2
            {
                PropId1 = 22,
                PropId2 = 33,
                PropId3 = 44
            }
        };
        var list3 = new List<Collection3>()
        {
            new Collection3
            {
                PropId1 = 111,
                PropId2 = 222,
                PropId3 = 333
            },
            new Collection3
            {
                PropId1 = 222,
                PropId2 = 333,
                PropId3 = 444
            }
        };

        var result = new ResultCollection(collection1, list2, list3);
        //or
        var result2 = new ResultCollection(collection1) //but in this case you have to change your constructor
        {
            Collection2s = list2,
            Collection3s = list3
        };
        Console.ReadLine();
    }
}

public class Collection1
{
    public int? PropId1 { get; set; }
    public int? PropId2 { get; set; }
    public int? PropId3 { get; set; }
}
public class Collection2
{
    public int? PropId1 { get; set; }
    public int? PropId2 { get; set; }
    public int? PropId3 { get; set; }
}
public class Collection3
{
    public int? PropId1 { get; set; }
    public int? PropId2 { get; set; }
    public int? PropId3 { get; set; }
}

public class ResultCollection : Collection1
{
    public ResultCollection() { }

    public ResultCollection(Collection1 collection, List<Collection2> list2, List<Collection3> list3)
    {
        foreach (PropertyInfo prop in collection.GetType().GetProperties())
        {
            PropertyInfo prop2 = collection.GetType().GetProperty(prop.Name);
            if (prop2.CanWrite)
                prop2.SetValue(this, prop.GetValue(collection, null), null);
        }
        Collection2s = list2;
        Collection3s = list3;
    }

    public List<Collection2> Collection2s { get; set; }

    public List<Collection3> Collection3s { get; set; }
}

Но можете ли вы привести пример входных данных?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...