如何编写LINQ查询,该查询采用分层源数据并对其进行转换以便分组被反转?
假设我有一个Topic对象列表,每个对象都包含一组标签,这些标签代表该主题的元数据标签。我需要的是编写一个LINQ查询来基本上翻转层次结构,这样我就有了一个标签列表,每个标签都有一组标记有特定标签的主题。
Topic { Title = "Political Debate #1", Posted = 01/02/2008 }
Tag { Name = "Contraversial", Color = "Red" }
Tag { Name = "Politics", Color = "LightBlue" }
Topic { Title = "iPhone to support SiliverLight!", Posted = 02/23/2009 }
Tag { Name = "BleedingEdge", Color = "LightBlue" }
Tag { Name = "Contraversial", Color = "Red" }
Tag { Name = ".NET", Color = "LightGreen" }
Topic { Title = "Fed Chairman admits guilt for causing second Great Depression", Posted = 06/15/2010 }
Tag { Name = "Politics", Color = "LightBlue" }
Tag { Name = "Contraversial", Color = "Red" }
我希望上面的数据看起来像下面的结果。
Tag { Name = "Contraversial", Color = "Red" }
Topic { Title = "Political Debate #1", Posted = 01/02/2008 }
Topic { Title = "iPhone to support SiliverLight!", Posted = 23/02/2009 }
Topic { Title = "Fed Chairman admits guilt for causing second Great Depression", Posted = 06/15/2010 }
Tag { Name = "Politics", Color = "LightBlue" }
Topic { Title = "Political Debate #1", Posted = 01/02/2008 }
Topic { Title = "Fed Chairman admits guilt for causing second Great Depression", Posted = 06/15/2010 }
Tag { Name = ".NET", Color = "LightGreen" }
Topic { Title = "iPhone to support SiliverLight!", Posted = 23/02/2009 }
您可以假设任何重复的数据都是唯一的,因为它是内存中的单个实例,并且这些数据只有几个对同一个对象的引用。使用匿名类生成投影的答案也是合理的,因为我意识到在反演之后类的形状可能会略有不同。
更新:我添加了下面的代码来设置示例数据。我正在玩LinqPad中发布的答案以及我自己的一些想法。
var tags = new[]
{
new { Name = "Contraversial", Color = "Red" },
new { Name = "Politics", Color = "LightBlue" },
new { Name = ".NET", Color = "LightGreen" },
new { Name = "BleedingEdge", Color = "LightBlue" }
};
var topics = new[]
{
new
{
Title = "Political Debate #1",
Posted = DateTime.Parse("01/02/2008"),
Tags = (from t in tags where new []{"Contraversial", "Politics"}.Contains(t.Name) select t),
},
new
{
Title = "iPhone to support SiliverLight!",
Posted = DateTime.Parse("02/23/2009"),
Tags = (from t in tags where new []{"BleedingEdge", "Contraversial", ".NET", }.Contains(t.Name) select t),
},
new
{
Title = "Fed Chairman admits guilt for causing second Great Depression",
Posted = DateTime.Parse("06/15/2010"),
Tags = (from t in tags where new []{"Contraversial", "Politics"}.Contains(t.Name) select t),
},
};
答案 0 :(得分:4)
您正在寻找的是 Pivot。
Is it possible to Pivot data using LINQ?
This source 包含Linq Pivot扩展方法的C#代码:
public static class LinqExtensions
{
public static Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>> Pivot<TSource, TFirstKey, TSecondKey, TValue>(this IEnumerable<TSource> source, Func<TSource, TFirstKey> firstKeySelector, Func<TSource, TSecondKey> secondKeySelector, Func<IEnumerable<TSource>, TValue> aggregate)
{
var retVal = new Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>>();
var l = source.ToLookup(firstKeySelector);
foreach (var item in l)
{
var dict = new Dictionary<TSecondKey, TValue>();
retVal.Add(item.Key, dict);
var subdict = item.ToLookup(secondKeySelector);
foreach (var subitem in subdict)
{
dict.Add(subitem.Key, aggregate(subitem));
}
}
return retVal;
}
}
答案 1 :(得分:0)
IDictionary<Topic, IList<Tag>> data;
var n = data.SelectMany(x => x.Value.Select(y => new { Topic = x.Key, Tag = y }))
.GroupBy(x => x.Tag, x => x.Topic);
答案 2 :(得分:0)
在LinqPad玩了一下后,我想我可能找到了合适的解决方案。
这是一个简单的例子。
var topicsByTags =
from topic in topics
from tag in topic.Tags
group topic by tag;
为了摆脱每个主题下的冗余标签集合,我们可以执行以下操作。
var topicsByTags =
from topic in topics
from tag in topic.Tags
group new
{
Title = topic.Title,
Color = topic.Posted,
} by tag into g
select new
{
g.Key.Name,
g.Key.Color,
Topics = g,
};
更新:下面是另一种利用投影中的分组本身的替代方案。上行是稍微清晰一点的查询,不足之处在于即使不使用该组,该组也会与该组保持一致。
var topicsByTags =
from topic in topics
from tag in topic.Tags
group new
{
Title = topic.Title,
Color = topic.Posted,
} by tag into g
select new
{
g.Key.Name,
g.Key.Color,
Topics = g,
};
我会推迟接受我自己的答案,以便就哪个解决方案解决我所提出的最佳问题进行辩论。