英文:
Problem sorting objects based on value of its properties
问题
这是您提供的代码部分的中文翻译:
我在这里有一个学校项目,我似乎无法弄清楚;我要创建一个控制台应用程序,允许用户输入一个虚构公司的销售人员数量。有关销售人员的信息包括他们的姓名、地区和销售物品的数量。然后,销售人员根据销售物品的数量被分为不同的级别。最后,销售人员将被打印到控制台。如果,例如,有两名销售人员达到级别1,有一名达到级别2,那么控制台应该看起来像这样:
John Johnsson,某地区,卖出33件物品
Mary Mara,另一地区,卖出40件物品
有2名销售人员达到级别1
Judy Juggernut,另一地区,卖出67件物品
有1名销售人员达到级别2
而问题就在于打印部分。当用户输入信息时,会创建一个销售人员类的新对象,并将其存储在销售人员数组中。然后检查每名销售人员的销售物品数量,并为每名销售人员分配一个级别。然后使用冒泡排序对数组进行排序,以使销售物品最少的销售人员位于salesmanArray[0]等位置。
一切都很好,直到要将结果打印到控制台。我尝试为此编写了一个方法:
```csharp
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");
}
//对于级别2、3和4也是相同的做法
}
我尝试做的是为不同级别创建4个新数组。然后,我循环遍历包含所有销售人员的数组,并根据销售物品的数量将销售人员放入相应的数组中。然后检查级别数组是否为空。如果不为空,我循环遍历它们,为每名销售人员打印姓名、地区和销售物品数量。最后还打印出每个级别中有多少销售人员。运行程序时,我在以下行上收到错误消息:
Console.WriteLine("Name: " + level1[i].name);
显示"System.NullReferenceException 已被引发“对象引用未设置为对象的实例”。
我认为这意味着level1[i].name没有引用到一个对象,但我不太知道接下来该怎么做... 任何建议或指导将不胜感激!
<details>
<summary>英文:</summary>
I'm sitting here with a school project I can't seem to figure out; I am to create a console application that let's the user enter a number of salesmen for a hypothetical company. The information about the salesmen includes their name, district and number of sold items. The salesmen are then to be sorted into different levels according to the number of sold items. Finally, the salesmen are to be printed to the console. The printing should be done one level at the time, if for instance two salesmen reached level one and one reached level 2, the console should look something like:
John Johnsson, someDistrict, 33 items sold
Mary Mara, someOtherDistrict, 40 items sold
2 salesmen reached level 1
Judy Juggernut, anotherDistrict, 67 items sold
1 salesmen reached level 2
And it's the printing part in question that gives me trouble. When the user enters information a new object of a salesman-class is created and stored in an array of salesmen. The number of items sold for each salesman is then checked and each salesman is assigned a level. The array is then sorted using bubblesort, to have the salesman with the least amount of sales on salesmanArray[0] and so on.
Everything works fine until its time to print the results to the console. I tried to write a method for it:
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
}
What I'm trying to do is 4 new arrays for the different levels. I then loop through the array with all the salesmen and place the salesmen into the arrays in accordance to the number of sold items. I then check if the level-arrays are empty. If they aren't, I loop through them printing out the name, district and items sold for each salesman. Finally also printing out how many sellers there are in each level. When running the program, I get an error on the line
Console.WriteLine("Name: " + level1[i].name);
Saying "System.NullReferenceException has been thrown "Object reference not set to an instance if an object".
I would assume that means level1[i].name isn't referencing to an object but I don't really know how to go from there... Any advice or pointers would be greatly appriciated!
</details>
# 答案1
**得分**: 2
由于您将级别数组的长度初始化为与销售员数组相同的长度,但只根据其级别向级别数组添加销售员,所以会出现`System.NullReferenceException`。因此,在级别数组中会有未初始化的`null`元素,当您尝试访问空元素的名称属性时,会导致异常,因为您尝试读取不存在元素的属性。
为了解决这个问题,您可以使用`List<Salesman>`而不是`Salesman[]`。[`List<T>`][1]是一个通用的动态数组,您可以以相同的方式迭代其项:
```csharp
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>
数组中,并以更简单的方式添加项目。此外,字符串插值是一种更简单、更快速和更可读的字符串连接语法。
// 在这里,我们创建了一个新的列表数组,并用4个空的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列表存储具有该级别的销售员
levels[salesmen.level - 1].Add(salesmen);
}
// 您可以使用嵌套循环迭代所有级别的销售员
for(int level = 0; level < levels.Length; level++)
{
foreach(var salesman in levels[level])
{
Console.WriteLine($"Name: {salesman.name}");
Console.WriteLine($"District: {salesman.district}");
Console.WriteLine($"Items sold: {salesman.itemsSold}");
}
// Count属性获取List<T>中包含的元素数,因此您不需要减少此值以显示具有此级别的销售员数
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}");
}
英文:
You are getting a System.NullReferenceException
because you are initializing the level arrays with the same length as the salesmen array, but you are only adding salesmen to the level arrays based on their level.
So there will be not initialized null
elements in the level arrays, and when you try to access the name property of a null element, you get the exception because you try to read property of absent element.
To fix this, you may use List<Salesman>
instead of Salesman[]
. List<T>
is a generic dynamic array and you can iterate over its items in the same way:
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
}
Here is some other improvments then you can do with your code. For example if Salesman.level may contains only values form the list [1, 2, 3, 4]
you can store levels in the List
of List<Salesman>
or in the array of List<Salesman>
and add items in the easier way. Also string interpolation is an easier, faster, and more readable string concatenation syntax.
// 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}");
}
Finally there is an interesting mechanism to manipulate collections in .NET called LINQ. You can use LINQ syntax to select, filter, group and aggregate data. LINQ is readable efficiency and powerful tool. Here's a sample of your code rewritten with 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}");
}
答案2
得分: 1
// 冒泡排序
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)
{
// 交换位置
//...
}
int counter = 0;
int lastLevel = 1; //如果1是最小级别
for (int i = 0; i < salesmenArray.Length; i++)
{
if(salesmenArray[j].level != lastLevel)
{
// 打印总结
//...
counter = 0; //重置计数器
}
// 打印详细信息
Console.WriteLine("姓名: " + level1[i].name);
Console.WriteLine("区域: " + level1[i].district);
Console.WriteLine("售出物品数量: " + level1[i].itemsSold);
counter++;
}
// 打印最后级别的最终总结
//...
英文:
Where is the bubble sort? Sort the array first, then loop through the array with counters to count each level and print the output from the same loop.
// 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
//...
The ... are lines for you to fill.
答案3
得分: 1
Vadim的回答详细解释了为什么你的代码失败。他提出了通过List
来解决问题的方法。我也会采用这种方法。
另一方面,你的方法是有效的,但不太高效,而且有一些陷阱(正如Vadim所提到的,你正在创建具有与销售人员总数相同大小的4级数组,然后通过i
将它们分配给每个级别,留下一些null
空隙)。如果你希望你的方法能够正常工作,在打印for
循环之前获取level1[i].name
之前,请检查level1[i]
是否不为null
。
如果你正在使用一个集成开发环境(IDE),我建议你在for
循环内部设置一个断点,然后查看level1
的内容。
学习愉快!
英文:
Vadim's answer details why your code is failing. He proposed a way to solve the problem via List
s. I would also follow that road.
On the other hand, your approach was valid, but not very efficient and has a few traps for yourself (as Vadim mentioned, you are creating 4 level arrays with the same size of the total of salesmen, then you assign them to each level via i
, leaving some null
gaps). If you want your approach to work, in the printing for
-loop, before getting level1[i].name
, check that level1[i]
is not null
.
If you are using an IDE, I would recommend you to put a breakpoint inside the for-loop and see the contents of level1
.
Good luck learning!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论