按最近的日期排序,并对相似的标题进行聚类(分组)



查找需要在日期字段上排序但也有相似标题的LINQ。考虑以下所需的排序:

Title                Date
"Some Title 1/3"     2009/1/3     "note1: even this is old title 3/3 causes this group to be 1st"
"Some Title 2/3"     2011/1/31    "note2: dates may not be in sequence with titles"
"Some Title 3/3"     2011/1/1     "note3: this date is most recent between "groups" of titles
"Title XYZ 1of2"     2010/2/1
"Title XYz 2of2"     2010/2/21

我展示了不同后缀的标题。如果一张海报的标题用了下面这样的东西怎么办?

"1 LINQ Tutorial"
"2 LINQ Tutorial"
"3 LINQ Tutorial"

查询如何识别这些标题相似?你不必解决所有问题,非常感谢第一个例子的解决方案。

谢谢。

附录#1 20110605@svick还提到,当他们的编号方案超过9时,标题作者通常不会考虑使用2位数。例如01、02…10、11等。

我看到的典型模式往往是前缀或后缀,甚至隐藏在中

1/10 1-10 ...
(1/10) (2/10) ...
1 of 10   2 of 10
Part 1  Part 2 ...

你也指出了一个有效的模式:

xxxx Tutorial : first session,  xxxx Tutorial : second session, ....

如果我有一个Levenstein函数StringDistance(s1,s2),我将如何适应LINQ查询:)

LINQ中的普通分组(和SQL中的分组,但这与这里无关)通过为集合中的每个元素选择一些键来工作。您没有这样的密钥,所以我不会使用LINQ,而是使用两个嵌套的foreaches:

var groups = new List<List<Book>>();
foreach (var book in books)
{
    bool found = false;
    foreach (var g in groups)
    {
        if (sameGroup(book.Title, g[0].Title))
        {
            found = true;
            g.Add(book);
            break;
        }
    }
    if (!found)
        groups.Add(new List<Book> { book });
}
var result = groups.Select(g => g.OrderBy(b => b.Date).ToArray()).ToArray();

这会逐渐创建一个组列表。将每本书与每组的第一本书进行比较。如果匹配,则将其添加到组中。如果没有匹配的组,则书本将创建一个新组。最后,我们使用带点符号的LINQ对结果进行排序。

如果将书籍与一组中的每本书进行比较,而不仅仅是第一本,那将是更正确的。但是你可能不会得到完全正确的结果,所以我认为这个优化是值得的

这具有时间复杂性O(N²),所以如果你有数百万本书,它可能不是最好的解决方案。

编辑:要对组进行排序,请使用类似的东西

groups.OrderBy(g => g.Max(b => b.Date))

对于按日期排序,您应该使用OrderBy运算符。

示例:

//Assuming your table is called Table in datacontext ctx
var data = from t in ctx.Table
           order by t.Date
           select t;

对于在相似性之后对字符串进行分组,您应该考虑类似于Hamming距离或Metaphone算法的东西。(尽管我不知道这些在.Net中的任何直接实现).

编辑:正如svick在评论中所建议的,Levenstein距离也可以被认为是Hamming距离的更好替代方案。

假设Title和Date字段包含在类调用模型中,请考虑以下类定义公共类模型

{
    public DateTime Date{get;set;}
    public string Title{get;set;}
    public string Prefix
    {get
        {
            return Title.Substring(0,Title.LastIndexOf(' '));
        }
    }
}

除了Date和Title属性外,我还创建了一个没有setter的前缀属性,它使用子字符串向我们返回公共前缀。您可以在这个属性的getter中使用您选择的任何方法。剩下的工作很简单。考虑一下这个Linqpad程序

void Main()
{
    var model = new List<Model>{new Model{Date = new DateTime(2011,1,3), Title = "Some Title 1/3"},
                new Model{Date = new DateTime(2011,1,1), Title = "Some Title 2/3"},
                    new Model{Date = new DateTime(2011,1,1), Title = "Some Title 3/3"},
                    new Model{Date = new DateTime(2011,1,31), Title = "Title XYZ 1of2"},
                    new Model{Date = new DateTime(2011,1,31), Title = "Title XYZ 2of2"}};
            var result = model.OrderBy(x => x.Date).GroupBy(x => x.Prefix);
            Console.WriteLine(result);
}

编辑>>如果我们把前缀放在一边,那么查询本身不会返回我想要的内容,即:1)按组的最近日期排序2)在集群中按标题排序。尝试以下

var model = new List<Model>{
                new Model{Date = new DateTime(2009,1,3), Title = "BTitle 1/3"},
                new Model{Date = new DateTime(2011,1,31), Title = "BTitle 2/3"},
                new Model{Date = new DateTime(2011,1,1), Title = "BTitle 3/3"},
                new Model{Date = new DateTime(2011,1,31), Title = "ATitle XYZ 2of2"},
                new Model{Date = new DateTime(2011,1,31), Title = "ATitle XYZ 1of2"}
                };
        var result = model.OrderBy(x => x.Date).GroupBy(x => x.Prefix);
        Console.WriteLine(result);

最新更新