C# - Использование LinQ для поиска повторяющихся элементов в списке и обновления свойств объекта, если это так

У меня есть список Allflights типа объекта FLightInfo.

Если объект из списка имеет одинаковый номер рейса и время взлета. Я хочу объединить эти записи и добавить их количество пользователей

Пример: Это мой список:

  • FlightNumber 123 Взлет 12:00 Пользователи 5
  • FlightNumber 256 Взлет 3:00 Пользователи 6
  • FlightNumber 123 Взлет 12:00 Пользователи 8
  • FlightNumber 651 Взлет 5:00 Пользователи 3

Я хочу, чтобы выход выглядел так:

FlightNumber 123  Takeoff 12:00 Users 13
FlightNumber 256  Takeoff 3:00  Users 6  
FlightNumber 651  Takeoff 5:00  Users 3

Мой исходный код:

struct FlightInfo
        {
            public string FlightNumber { get; set; }
            public string Takeoff_time { get; set; }
            public string Landing_time { get; set; }
            public int UserCount { get; set; }
}

static List<FlightInfo> allFlights = new List<FlightInfo>();

//I read several files using multi-threading and create a FlightInfo object   
//and add it to the allFlights list

allFlights.Add(buildFlight(FlightNumber, Origination, Destination, Takeoff_time, Landing_time, UserCount);

//This is what I need to do
//if FlightNumber && testTakeOff object attributes are the same 
// I want to consolidate those entries and add the UserCount from those entries

Можете попробовать перед добавлением проверить? Как бы Вы это сделали? Вы можете создать класс, который принимает объекты FlightInfo, а затем выполняет проверку / консолидацию. Если вы действительно добавляете из нескольких потоков, вам следует рассмотреть возможность использования оператора lock. Бонус: ваш новый класс может применить эту блокировку!

Dennis Kuypers 22.10.2018 19:42

Не могли бы вы добавить несколько простых данных и ожидаемого результата?

HimBromBeere 22.10.2018 19:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
210
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я сделал ваш FlightInfo классом и переименовал некоторые свойства в соответствии со стилями кода C#. Также я добавил конструктор, который принимает данные.

class FlightInfo
{
    public FlightInfo(string flightNumber, string origination, ...)
    {
        FlightNumber = flightNumber;
        Origination = origination;
        // ...
    }

    public string FlightNumber { get; set; }
    public string Origination { get; set; }
    public string Destination { get; set; }
    public string TakeoffTime { get; set; }
    public string LandingTime { get; set; }
    public int UserCount { get; set; }
}

Мы добавляем класс FlightManager, который заботится о

  1. предотвращение любых проблем с многопоточностью с помощью оператора блокировки
  2. объединение записей

FlightManager

class FlightManager
{
    private object _lock = new object();
    private List<FlightInfo> _flights = new List<FlightInfo>();

    public void Add(FlightInfo info)
    {
        lock(_lock)
        {
            // look for existing flights
            var existing = _flights.FirstOrDefault(f =>
            {
                return f.FlightNumber == info.FlightNumber
                    && f.TakeoffTime == info.TakeoffTime;
            });

            // FirstOrDefault will return null if none found
            if (existing == null)
            {
                // add as new flight
                _flights.Add(info);
            }
            else
            {
                // add passenger count
                existing.UserCount += info.UserCount;
            }
        }
    }
}

использование

static FlightManager Manager = new FlightManager();

allFlights.Add(new FlightInfo(FlightNumber, Origination, Destination, TakeoffTime, LandingTime, UserCount);

POCOs (объекты данных)

Существует альтернативный синтаксис для инициализации вашего FlightInfo, если вы отбрасываете конструктор.

var flightInfo = new FlightInfo()
{
    Origination = origination,
    Destination = destination,
    // ...
}

но конструктор проясняет ваше намерение, что пользователь должен предоставить все данные заранее. Однако, если вы используете библиотеку сериализации, это может быть невозможно.

Есть еще несколько методов, например, вы можете использовать ConcurrentDictionary и его метод AddOrUpdate для этого.

Dennis Kuypers 22.10.2018 20:05
Ответ принят как подходящий

То, что вы ищете, может быть выполнено с помощью GroupBy<TSource,TKey,TElement,TResult>(IQueryable<TSource>, Expression<Func<TSource,TKey>>, Expression<Func<TSource,TElement>>, Expression<Func<TKey,IEnumerable<TElement>,TResult>>, IEqualityComparer<TKey>)

Используя определение вашего объекта, вы можете сделать что-то вроде этого

var consildatedFlights = allFlights.GroupBy(x => new {x.FlightNumber, x.Takeoff_time}, x => x,
            (key, vals) => ConsolidateFlightInfo(vals));

foreach(var flight in consildatedFlights)
    Console.WriteLine($"FlightNumber: {flight.FlightNumber}, Takeoff Time: {flight.Takeoff_time}, User Count: {flight.UserCount}");

public static FlightInfo ConsolidateFlightInfo(IEnumerable<FlightInfo> flights)
{
    var list = flights.ToList();
    var ret = list[0];
    ret.UserCount = list.Sum(x => x.UserCount);
    return ret;
}

Первый аргумент .GroupBy указывает анонимный тип, описывающий свойства, по которым вы хотите группировать. Второй элемент указывает, что вы хотите в списках результатов (по одному на группу). В этом случае нам нужен объект информации о полете целиком. Третий параметр указывает, как вы хотите преобразовать каждую группу. В этом случае мы передаем каждый набор сгруппированных рейсов в метод, который суммирует UserCount и возвращает один FlightInfo с этим суммированным значением.

Немного поздно, но спасибо вам за это !! это сработало отлично. Я просто пометил ваш ответ как ответ :)

Fernando 04.06.2020 01:17

Другие вопросы по теме