Проблема сортировки объектов по значению их свойств

Я сижу здесь со школьным проектом, в котором не могу разобраться; Я должен создать консольное приложение, которое позволит пользователю ввести количество продавцов для гипотетической компании. Информация о продавцах включает их имя, район и количество проданных товаров. Затем продавцов нужно отсортировать по разным уровням в зависимости от количества проданных товаров. Наконец, продавцы должны быть выведены на консоль. Печать должна выполняться по одному уровню за раз, если, например, два продавца достигли уровня 1, а один – уровня 2, консоль должна выглядеть примерно так:

John Johnsson, SomeDistrict, продано 33 шт. Мары Мара, SomeOtherDistrict, продано 40 шт. 2 продавца достигли уровня 1

Джуди Джаггернат, другой район, продано 67 шт. 1 продавец достиг уровня 2

И именно та часть печати, о которой идет речь, доставляет мне неприятности. Когда пользователь вводит информацию, создается новый объект класса продавцов, который сохраняется в массиве продавцов. Затем проверяется количество товаров, проданных каждым продавцом, и каждому продавцу присваивается уровень. Затем массив сортируется с помощью пузырьковой сортировки, чтобы иметь продавца с наименьшим объемом продаж в salesmanArray[0] и так далее.

Все работает нормально, пока не пришло время вывести результаты на консоль. Я попытался написать для него метод:

    public static void sortering(Salesman[] salesmenArray)
    {
        Salesman[] level1 = new Salesman[salesmenArray.Length];
        Salesman[] level2 = new Salesman[salesmenArray.Length];
        Salesman[] level3 = new Salesman[salesmenArray.Length];
        Salesman[] level4 = new Salesman[salesmenArray.Length];

        for (int i = 0; i < salesmenArray.Length - 1; i++)
        {
            if (salesmenArray[i].level == 1)
            {
                level1[i] = salesmenArray[i];

            } else if (salesmenArray[i].level == 2)
            {
                level2[i] = salesmenArray[i];

            } else if (salesmenArray[i].level == 3)
            {
                level3[i] = salesmenArray[i];

            } else if (salesmenArray[i].level == 4)
            {
                level4[i] = salesmenArray[i];
            }
        }

        if (level1.Length != 0)
        {
            for (int i = 0; i < level1.Length - 1; i++)
            {
                Console.WriteLine("Name: " + level1[i].name);
                Console.WriteLine("District: " + level1[i].district);
                Console.WriteLine("Items sold: " + level1[i].itemsSold);
            }
            Console.WriteLine("" + (level1.Length - 1) + " sellers have reached level 1");
        }
            //Same thing for level 2, 3 and 4
    }

Я пытаюсь сделать 4 новых массива для разных уровней. Затем я перебираю массив со всеми продавцами и помещаю продавцов в массивы в соответствии с количеством проданных товаров. Затем я проверяю, пусты ли массивы уровней. Если это не так, я перебираю их, распечатывая имя, район и товары, проданные для каждого продавца. Наконец, также распечатайте количество продавцов на каждом уровне. При запуске программы получаю ошибку в строке

Console.WriteLine("Name: " + level1[i].name);

Высказывание «System.NullReferenceException было брошено» «Ссылка на объект не установлена ​​​​на экземпляр, если это объект».

Я бы предположил, что это означает, что level1[i].name не ссылается на объект, но я действительно не знаю, как оттуда идти... Буду очень признателен за любые советы или указатели!

Может оказаться полезным следующее: IComparable и IComparer

user09938 18.02.2023 20:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
54
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы получаете System.NullReferenceException, потому что вы инициализируете массивы уровней той же длины, что и массив продавцов, но вы добавляете продавцов в массивы уровней только на основе их уровня.

Таким образом, в массивах уровней будут неинициализированные null элементы, и когда вы попытаетесь получить доступ к свойству name нулевого элемента, вы получите исключение, потому что вы пытаетесь прочитать свойство отсутствующего элемента.

Чтобы исправить это, вы можете использовать List<Salesman> вместо Salesman[]. List<T> — это общий динамический массив, и вы можете перебирать его элементы таким же образом:

public static void sortering(Salesman[] salesmenArray)
{
    var level1 = new List<Salesman>();
    var level2 = new List<Salesman>();
    var level3 = new List<Salesman>();
    var level4 = new List<Salesman>();

    for (int i = 0; i < salesmenArray.Length; i++)
    {
        if (salesmenArray[i].level == 1)
        {
            level1.Add(salesmenArray[i]);
        }
        else if (salesmenArray[i].level == 2)
        {
            level2.Add(salesmenArray[i]);
        }
        else if (salesmenArray[i].level == 3)
        {
            level3.Add(salesmenArray[i]);
        }
        else if (salesmenArray[i].level == 4)
        {
            level4.Add(salesmenArray[i]);
        }
    }

    if (level1Count > 0)
    {
        for (int i = 0; i < level1.Count; i++)
        {
            Console.WriteLine("Name: " + level1[i].name);
            Console.WriteLine("District: " + level1[i].district);
            Console.WriteLine("Items sold: " + level1[i].itemsSold);
        }
        Console.WriteLine("" + level1Count + " sellers have reached level 1");
    }
    //Same thing for level 2, 3 and 4
}

Вот некоторые другие улучшения, которые вы можете сделать со своим кодом. Например, если Salesman.level может содержать только значения из списка [1, 2, 3, 4], вы можете хранить уровни в List списка List<Salesman> или в массиве List<Salesman> и добавлять элементы более простым способом. Также интерполяция строк — это более простой, быстрый и читаемый синтаксис конкатенации строк.

// here we creates a new array of lists and initialize it with 4 empty lists of Salesman
var levels = new List<Salesman>[] 
{
    new List<Salesman>(),
    new List<Salesman>(),
    new List<Salesman>(),
    new List<Salesman>()
};

foreach(var salesmen in salesmenArray)
{
    // (salesmen.level - 1)-th list stores salesmen with that level
    levels[salesmen.level - 1].Add(salesmen);
}

// you can iterate salesmen of all levels with nested loops 
for(int level = 0; level < levels.Lenth; level++)
{
    foreach(var salesman in levels[level])
    {
        Console.WriteLine($"Name: {salesman.name}");
        Console.WriteLine($"District: {salesman.district}");
        Console.WriteLine($"Items sold: {salesman.itemsSold}");
    }

    // Count property gets the number of elements contained in the List<T> so you don't need to decrement this value for display the number of salesmen with this level 
    Console.WriteLine($"{levels[level].Count} sellers have reached level {level + 1}");
}

Наконец, есть интересный механизм управления коллекциями в .NET под названием LINQ. Вы можете использовать синтаксис LINQ для выбора, фильтрации, группировки и агрегирования данных. LINQ — это читаемая эффективность и мощный инструмент. Вот пример вашего кода, переписанного с помощью LINQ:

foreach(var group in salesmenArray
    .GroupBy(salesman => salesman.level)
    .OrderBy(groups => groups.Key))
{
    foreach(var salesman in group)
    {
        Console.WriteLine($"Name: {salesman.name}");
        Console.WriteLine($"District: {salesman.district}");
        Console.WriteLine($"Items sold: {salesman.itemsSold}");
    }
    Console.WriteLine($"{group.Count()} sellers have reached level {group.Key}");
}

Большое спасибо за объяснение и советы! Я попытался изучить ошибку перед публикацией, но мне было трудно применить то, что было сказано, к моей проблеме, но теперь я ясно это понимаю!

pinkRobot435 19.02.2023 06:02

@pinkRobot435, если вы выдаете этот код как свой, вы совершаете плагиат.

Strom 19.02.2023 06:11

Где пузырьковая сортировка? Сначала отсортируйте массив, затем выполните цикл по массиву со счетчиками для подсчета каждого уровня и распечатайте результат того же цикла.

   // bubble sort
   for (int i = 0; i < salesmenArray.Length; i++)
      for (int j = 0; j < salesmenArray.Length - 1; j++)
         if (salesmenArray[j].itemsSold > salesmenArray[j+1].itemsSold)
         {
            //swap positions
            //...
         }
   int counter = 0;
   int lastLevel = 1; //if 1 is the min level
   for (int i = 0; i < salesmenArray.Length; i++)
   {
       if (salesmenArray[j].level != lastLevel)
       {
          //print summary
          //...
         counter = 0; //reset counter
       }
       // print detail lines
       Console.WriteLine("Name: " + level1[i].name);
       Console.WriteLine("District: " + level1[i].district);
       Console.WriteLine("Items sold: " + level1[i].itemsSold);

       counter++;
   }
   //print final summary for last level
          //...

... это строки, которые вы должны заполнить.

Большое спасибо за ваш ответ! Я не включил пузырьковую сортировку, так как сохранил ее в отдельном методе и хотел быть максимально кратким. Я на самом деле попробовал ваш подход, прежде чем придумать решение, которое у меня есть раньше, но не мог понять, как заставить его работать... Приятно видеть, как это можно сделать, спасибо

pinkRobot435 19.02.2023 06:05

В ответе Вадима подробно описано, почему ваш код не работает. Он предложил способ решить проблему с помощью Lists. Я бы тоже пошел по этому пути.

С другой стороны, ваш подход был правильным, но не очень эффективным и имеет несколько ловушек для себя (как упомянул Вадим, вы создаете 4-уровневые массивы с одинаковым размером общего количества продавцов, затем вы назначаете их каждому уровню через i, оставив несколько null пробелов). Если вы хотите, чтобы ваш подход работал, в цикле печати for перед получением level1[i].name убедитесь, что level1[i] не является null.

Если вы используете IDE, я бы порекомендовал вам поставить точку останова внутри цикла for и посмотреть содержимое level1.

Удачи в обучении!

Большое спасибо за Ваш ответ! Я понимаю, что мое решение довольно неэффективно... Сначала я попытался найти подход, похожий на то, что предложил Стром, но не смог заставить его работать и вместо этого решил сделать что-то более простое. Но я думаю, в этом вся прелесть обучения, я очень рад, что понял ошибку

pinkRobot435 19.02.2023 06:07

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