使用LINQ对列表进行排序或排序并输出到列表框中

时间:2019-02-14 13:56:41

标签: c# visual-studio linq

我试图将文本文件分成4列(以逗号分隔),并使用Streamreader读取它,然后找到第一列中最流行的出现并将其输出到列表框中。

我已经在线尝试了LINQ建议,但是我认为问题在于阅读文本文件和尝试对其进行排序时的格式之间。

我有一个地点列表,我想要在列表中最常出现的地点。当我只输出无序的列表时,它就会输出到列表框中。

我遇到的错误是“ char”没有“ col”的定义?

输入文件,例如:
姓名,年龄,出生日期,男性
安妮,1991年1月28日,假
安妮,1989年6月29日,假
约翰,2000年6月18日,是

我想在列表框中输出:“ Anne”:

private void btnPopularCourse_Click(object sender, EventArgs e)
{
            StreamReader sr = new StreamReader(@"R:\Data.txt");
            string line = string.Empty;

            while ((line = sr.ReadLine()) != null)
            {

                lstMostPopularCourse.ClearSelected();
                string[] col = line.Split(',');

                var sort = line.GroupBy(item => item.col[0]);

                var popular = sort.OrderByDescending(group => group.Count());

                lstMostPopularCourse.Items.Add(popular.First());


            }

            sr.Close();
}


private void btnPopularCourse_Click(object sender, EventArgs e)
{
            StreamReader sr = new StreamReader(@"R:\Data.txt");
            string line = string.Empty;

            while ((line = sr.ReadLine()) != null)
            {

                lstMostPopularCourse.ClearSelected();
                string[] col = line.Split(',');

                var popular = (from item in col[0]
                group item by item into gr
                orderby gr.Count() descending
                select gr.Key).First();

                lstMostPopularCourse.Items.Add(popular);

            }

           sr.Close();
}

2 个答案:

答案 0 :(得分:2)

我认为,这就是您要寻找的

static void Main(string[] args)
{
    var nameList = new List<string>();
    foreach (string line in File.ReadLines(@"YOUR PATH"))
    {
        var data = line.Split(',');
        nameList.Add(data[0]);
    }

    var mostFrequentName = nameList.GroupBy(x => x)
        .Where(g => g.Count() > 1)
        .OrderByDescending(g => g.Count())
        .Select(g => g.Key)
        .FirstOrDefault();
    }
}

答案 1 :(得分:1)

我认为您应该separate your concerns。分开获取数据的方式(从CSV文件中读取),处理数据的方式(查找第一列中最流行的出现方式)以及显示日期的方式(放入显示项中,这种情况是一个列表框)

关注点分离有几个优点:

  • 由于您的代码专注于一个主题,因此更易于理解。
  • 该代码将更易于测试,而无需其他方面的开销。
  • 该代码可以重复使用。您可以使用CSV文件读取内容来填充其他项,例如表格
  • 如果其中一个问题发生了变化,例如,您从数据库而不是CSV文件中读取数据,则无需更改很多代码。

您说过您有LINQ问题,所以我想您在读取输入数据时不会有问题。您将具有以下代码:

class Person
{
    public string Name {get; set;}
    public DateTime Dob {get; set;}
    public bool Male {get; set;}
    public int Age => (DateTime.Now - this.Dob).Years // almost correct, TODO: repair
}

IEnumerable<Person> ReadPersons() {...}  // reads from your input file

类似地,您将具有一些将Person添加到向操作员显示Persons的对象的功能。在您的情况下为ListBox,但是由于我们分离了关注点,因此我们不在乎。毕竟,您说您已经掌握了添加它的能力。

void DislayName(string name) {... add name to the listbox }

现在,您要做的就是创建一个函数,用于选择要显示的人员的姓名。

string SelectNameToDisplay(IEnumerable<Person> persons)
{
    TODO: implement
}

private void btnPopularCourse_Click(object sender, EventArgs e)
{
     IEnumerable<Person> persons = ReadPersons();
     string nameToDisplay = SelectNameToDisplay(persons);
     DisplayName(nameToDisplay);
}

您表示阅读人员并显示姓名没有问题,所以我们要做的就是选择正确的姓名

我添加了...或者默认,如果没有人

string SelectNameToDisplayOrDefault(IEnumerable<Person> persons)
{
    // TODO: exception if persons equals null

    // select the person with the name that occurs most
    // group them by name, and count the number of persons in each group
    // finally take the group with the largest number of persons

    var mostUsedName = persons
        .GroupBy(person => person.Name,       // KeySelector
            (name, personsWithThisName) => new // ResultSelector
            {
                Name = name,
                Count = personsWithThisName.Count(),
            })

         // order such that the person that occurs most comes first
         .OrderByDescending(person => person.Count)
         // keep only the name:
         .Select(person => person.Name)
         // and take the first one
         .FirstOrDefault();
     return mostUsedName;             
}

尽管这将在一个LINQ语句中完成这项工作,但是如果只需要在排序之后排在第一位的Person,那么按Count对所有Person进行排序会浪费处理能力。我会稍作更改:

string SelectNameToDisplayOrDefault(IEnumerable<Person> persons)
{
    var personsWithCount = persons
        .GroupBy(person => person.Name,       // KeySelector
            (name, personsWithThisName) => new // ResultSelector
            {
                Name = name,
                Count = personsWithThisName.Count(),
            });

    // get the person that has the highest count, enumerating only once:
    var personEnumerator = personsWithCount.GetEnumerator();
    if (personEnumerator.MoveNext())
    {
        // there is at least one Person:
        var mostOftenCountedPerson = personEnumerator.Current;

        // check if other persons have a higher count:
        while (personEnumerator.MoveNext())
        {
            // there is a next Person; does he have a higher Count?
            if (personEnumerator.Current.Count > mostOftenCountedPerson.Count)
            {
                 // yes this person is counted more often
                 mostOftenCountedPerson = personEnumerator.Current;
            }
        }

        // enumerated the sequence exactly once, and we know the Person that is counted most
        return mostOftenCountedPerson;
    }
    else
    {   
        // no person at all; TODO: decide what to do
    }
}