我知道如何使用LINQ对数据进行分组,我知道如何将其拆分为单独的项目,但我不知道如何仅将其部分取消组合。
我有一组看起来像这样的数据:
var data = new Dictionary<Header, Detail>()
{
{ new Header(), new Detail { Parts = new List<string> { "Part1", "Part1", "Part2" } } }
};
为了正确处理这个问题,我需要复制部分的每个实例都是字典中的单独条目(尽管如果它仍然是字典并不重要 - IEnumerable<KeyValuePair<Header, Detail>>
是完全可以接受的)。但是,我不想完全拆分Parts
列表 - 列表中的不同部分很好。
具体来说,我希望最终数据看起来像这样:
{
{ new Header(), new Detail { Parts = new List<string> { "Part1", "Part2" } } },
{ new Header(), new Detail { Parts = new List<string> { "Part1" } } },
}
更复杂的例子:
var data = new Dictionary<Header, Detail>()
{
{ new Header(1), new Detail { Parts = new List<string> { "Part1", "Part1", "Part2" } } },
{ new Header(2), new Detail { Parts = new List<string> { "Part1", "Part2" } } },
{ new Header(3), new Detail { Parts = new List<string> { "Part1", "Part2", "Part2", "Part2", "Part3", "Part3"} } }
};
var desiredOutput = new List<KeyValuePair<Header, Detail>>()
{
{ new Header(1), new Detail { Parts = new List<string> { "Part1", "Part2" } } },
{ new Header(1), new Detail { Parts = new List<string> { "Part1" } } },
{ new Header(2), new Detail { Parts = new List<string> { "Part1", "Part2" } } },
{ new Header(3), new Detail { Parts = new List<string> { "Part1", "Part2", "Part 3" } } },
{ new Header(3), new Detail { Parts = new List<string> { "Part2", "Part3" } } },
{ new Header(3), new Detail { Parts = new List<string> { "Part2" } } }
};
有什么建议吗?
答案 0 :(得分:2)
不,实际上没有一个LINQ函数可以完成所有这些。
基本上,如果您想要按每个字符串对Parts
进行分组,并将每个组视为一行,您想要的是每个“列”。我使用辅助函数GetNthValues
(用于模拟LINQ函数样式)来完成此操作。一旦你拥有了它,它几乎只是在每个部分进行分组,调用函数,并将结果放回字典中。
public static Dictionary<Header, Detail> Ungroup(Dictionary<Header, Detail> input)
{
var output = new Dictionary<Header, Detail>();
foreach (var key in input.Keys)
{
var lookup = input[key].Parts.ToLookup(part => part);
bool done = false;
for (int i = 0; !done; i++)
{
var parts = lookup.GetNthValues(i).ToList();
if (parts.Any())
{
output.Add(new Header(key.Value), new Detail { Parts = parts });
}
else
{
done = true;
}
}
}
return output;
}
public static IEnumerable<TElement> GetNthValues<TKey, TElement>(
this ILookup<TKey, TElement> source, int n)
{
foreach (var group in source)
{
if (group.Count() > n)
{
yield return group.ElementAt(n);
}
}
}
答案 1 :(得分:2)
Linq在这里对你没什么帮助,但这里有一个扩展方法,可以解决这个问题:
public static IEnumerable<KeyValuePair<Header, Detail>> UngroupParts(
this IEnumerable<KeyValuePair<Header, Detail>> data)
{
foreach (var kvp in data)
{
Header header = kvp.Key;
List<string> parts = kvp.Value.Parts.ToList();
do
{
List<string> distinctParts = parts.Distinct().ToList();
Detail detail = new Detail() { Parts = distinctParts };
yield return new KeyValuePair<Header, Detail>(header, detail);
foreach (var part in distinctParts)
parts.Remove(part);
}
while (parts.Any());
}
}
用法:
var desiredOutput = data.UngroupParts();
答案 2 :(得分:1)
从SortedSet
部分的元素中创建Detail
。转换为List
的这是您的第一个组,事实上SortedSet
实际上只包含Detail
中每个元素的一个实例。
将其从原始Detail
部分(或其副本)中删除。重复,直到细节大小为零。
编辑:
尝试使用类似于单个Linq语句的内容。让我使用列表来简化
var total = new List<List<string>>() {
new List<string>(),
new List<string>(),
new List<string>(),
new List<string>(),
new List<string>(),
new List<string>()
};
//the statement
var q = k.Aggregate(total, (listOlists, singleStrin) => {
listOlists.Where(l => !l.Contains(singleStrin)).First().Add(singleStrin);
return listOlists;
});
基本上我创建了一个累加器函数,只有当列表中不包含元素时才会将元素添加到字符串列表中。列表本身包含在累加器列表中。你需要初始化累加器列表,否则Linq语句会变得更加丑陋。
答案 3 :(得分:0)
这会将字符串列表分成多个字符串列表而不会重复。
List<string> oldParts = new List<string> { "Part1", "Part2", "Part2", "Part2", "Part3", "Part3" };
List<List<string>> allLists = new List<List<string>>();
foreach (string currentPart in oldParts)
{
foreach (List<string> currentList in allLists)
{
// if currentList doesn't have the part, then
// add part to the currentList, and process next part
if (!currentList.Contains(currentPart))
{
currentList.Add(currentPart);
goto NextPart;
}
}
// if we get here, the part is already contained on in the lists
// so add a new list to allLists
// and add the part to the new list
allLists.Add(new List<string> { currentPart });
NextPart: ;
}