从结果列表中获取唯一数据

时间:2018-12-09 08:46:05

标签: c# linq sorting partition

我有类似这样的数据。

原始数据:
1-> A-> 1-> 2011-> 2009
2-> A-> 1-> 2011-> 2010
3-> B-> 1-> 2008-> 2008
4-> B-> 1-> 2009-> 2009
5-> A-> 2-> 2008-> 2009
6-> B-> 1-> 2008-> 2011

第1步:按列2进行分区。
1-> A-> 1-> 2011-> 2009
2-> A-> 1-> 2011-> 2010
5-> A-> 2-> 2008-> 2009
3-> B-> 1-> 2008-> 2008
4-> B-> 1-> 2009-> 2009
6-> B-> 1-> 2008-> 2011

第2步:按第3列(降序)应用排序,如果在第一个位置找到唯一记录,则返回该结果,否则仅对不明确的记录应用下一个排序条件,即重复第2步。
5-> A-> 2 -> 2008-> 2009 //唯一记录
1-> A-> 1 -> 2011-> 2009
2-> A-> 1-> 2011-> 2010
3-> B-> 1 -> 2008-> 2008
4-> B-> 1 -> 2009-> 2009
6-> B-> 1 -> 2008-> 2011

所以现在我们的结果将是这样。 清单1:
5-> A-> 2-> 2008-> 2009

列表2:由于我们已经能够在“ A”中找到唯一记录,因此从剩余列表中删除其数据
3-> B-> 1-> 2008-> 2008
4-> B-> 1-> 2009-> 2009
6-> B-> 1-> 2008-> 2011

第3步:应用下一个排序标准,即column4(降序),但位置1的数据不明确
3-> B-> 1-> 2008 -> 2008
6-> B-> 1-> 2008 -> 2011
4-> B-> 1-> 2009-> 2009

第4步:仅对不明确的结果应用下一个排序条件,即column5(降序)
6-> B-> 1-> 2008-> 2011 //唯一记录
3-> B-> 1-> 2008-> 2008

因此最终列表将包含以下结果。
5-> A-> 2-> 2008-> 2009
6-> B-> 1-> 2008-> 2011

因此,我想在应用每种排序标准后从结果中获取唯一数据。我不知道我是否能够以更好的方式解释我的问题,我知道通过使用for循环并比较数据我可以解决它,但只需要帮助就可以最好地解决它。

3 个答案:

答案 0 :(得分:1)

您可以在此使用linq:

namespace ConsoleApp4
{
    class SortOrder
    {
    public Func<DataRec, object> PropName { get; set; }
    public bool Ascending { get; set; }
    }

    public class DataRec
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Order { get; set; }
        public int Year1 { get; set; }
        public int Year2 { get; set; }
    }
    class Program
    {


        static void Main(string[] args)
        {
           List<DataRec> myData = new List<DataRec>();

        myData.Add(new DataRec() { ID = 1, Name = "A", Order = 1, Year1 = 2011, Year2 = 2009 });
        myData.Add(new DataRec() { ID = 2, Name = "A", Order = 1, Year1 = 2011, Year2 = 2010 });
        myData.Add(new DataRec() { ID = 3, Name = "B", Order = 1, Year1 = 2008, Year2 = 2008 });
        myData.Add(new DataRec() { ID = 4, Name = "B", Order = 1, Year1 = 2009, Year2 = 2009 });
        myData.Add(new DataRec() { ID = 5, Name = "A", Order = 2, Year1 = 2008, Year2 = 2009 });
        myData.Add(new DataRec() { ID = 6, Name = "B", Order = 1, Year1 = 2008, Year2 = 2011 });

        //var orderedData = myData.GroupBy(x=>x.Name, (key, group) =>group.OrderByDescending(x => x.Order).ThenBy(x => x.Year1).ThenByDescending(x => x.Year2).First()).ToList();
        List<SortOrder> sorting = new List<SortOrder>();
        sorting.Add(new SortOrder() { PropName = x => x.Order, Ascending = false });
        sorting.Add(new SortOrder() { PropName = x => x.Year1, Ascending = true });
        sorting.Add(new SortOrder() { PropName = x => x.Year2, Ascending = false });

        var orderedData = myData.GroupBy(x=>x.Name);

        IOrderedEnumerable<DataRec> sorted;
        List<DataRec> result = new List<DataRec>();

        foreach (var oneGroup in orderedData)
        {
            sorted = null;
            foreach (SortOrder oneSort in sorting)
            {
                if (sorted == null)
                {
                    sorted = oneSort.Ascending ? oneGroup.OrderBy(oneSort.PropName) : oneGroup.OrderByDescending(oneSort.PropName);                      
                }
                else
                {
                    sorted = oneSort.Ascending ? sorted.ThenBy(oneSort.PropName): sorted.ThenByDescending(oneSort.PropName);
                }
            }
            result.Add(sorted.First());
        }
    }

首先我将其分组(您称它为partition),然后我根据您的需求进行排序,使其变得动态并获得第一条记录。

答案 1 :(得分:0)

对我来说,最好的方法是,这种方法涉及的循环次数最少:

第2列上需要一个LINQ组(第1步:按第2列进行分区。)

这将为您提供两个组的匿名列表,其中包含组[0]中A的所有元素和组[1]中B的所有元素。

因此,您需要在组的数组上循环并在对第3列降序然后按第4列降序然后按第5列进行排序之后采用第一个元素。这里的重要部分是ThenBy。

所以基本上可以归结为:

For each objGroup in dtb.Group(....)
objGroup.OrderByDescending(Column3)
.ThenByDescending(Column4)
.ThenByDescending(Column5).

让我知道您是否需要正确的语法。

答案 2 :(得分:0)

我创建了一个类似于以下的类来表示您的数据

public class TestClass
    {
        private int _col1;
        private char _col2;
        private int _col3;
        private int _col4;
        private int _col5;

        public TestClass(int c1, char c2, int c3, int c4, int c5)
        {
            _col1 = c1;
            _col2 = c2;
            _col3 = c3;
            _col4 = c4;
            _col5 = c5;
        }

        public int Col1
        {
            get { return _col1; }
            set { _col1 = value; }
        }

        public char Col2
        {
            get { return _col2; }
            set { _col2 = value; }
        }

        public int Col3
        {
            get { return _col3; }
            set { _col3 = value; }
        }

        public int Col4
        {
            get { return _col4; }
            set { _col4 = value; }
        }

        public int Col5
        {
            get { return _col5; }
            set { _col5 = value; }
        }
    }

然后,我编写了该程序,该程序似乎可以在一定程度上满足您的需求

List<TestClass> test = new List<TestClass>();

            test.Add(new TestClass(1, 'A', 1, 2011, 2009));
            test.Add(new TestClass(2, 'A', 1, 2011, 2010));
            test.Add(new TestClass(3, 'B', 1, 2008, 2008));
            test.Add(new TestClass(4, 'B', 1, 2009, 2009));
            test.Add(new TestClass(5, 'A', 2, 2008, 2009));
            test.Add(new TestClass(6, 'B', 1, 2008, 2011));

            var first_ordered = from t in test
                                orderby t.Col2, t.Col3 descending, t.Col4 descending, t.Col5 descending
                                group new { t.Col1, t.Col3, t.Col4, t.Col5 } by t.Col2 into p
                                select new
                                {
                                    Col1 = p.First().Col1,
                                    Col2 = p.Key,
                                    Col3 = p.First().Col3,
                                    Col4 = p.First().Col4,
                                    Col5 = p.First().Col5
                                };

            foreach(var f in first_ordered) 
            {
                Console.WriteLine($"{f.Col1}, {f.Col2}, {f.Col3}, {f.Col4}, {f.Col5}");
            }

它适用于'A'分区,不适用于'B'分区,但是您的解释中似乎有些不正确的地方:在文本中描述步骤3时,您需要输入4降序排列,但在您输入的数据中将其升序排列。因此,我按照您编写的文本进行操作,如果要升序,您只需获取我的代码,请删除orderby子句中的“ descending”,Linq会自动将提到的字段升序。 希望对您有所帮助!