从 C# 中缺少键的列表创建完整集
Create complete set from a list with missing keys in C#
我已经做了很多挖掘工作,但似乎找不到这个特定问题的答案;类似问题的答案,但不是这样。
本质上,我要做的是在列表中添加具有默认值的缺失键。我有一个 List>() 结构如下:
键:值
name : Orange
actualname : Orango
name : Lime
fullname : Lime Lime
actualname : Limo
从上面的列表中,我想创建一个完整的集合,其中缺少密钥。
预期键:值
name : Orange
fullname : ""
actualname : Orango
name : Lime
fullname : Lime Lime
actualname : Limo
我正在尝试以下代码:
var list = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("name", "Orange"),
new KeyValuePair<string, string>("actualname", "Orango"),
new KeyValuePair<string, string>("name", "Lime"),
new KeyValuePair<string, string>("fullname", "Lime Lime"),
new KeyValuePair<string, string>("actualname", "Limo")
};
var distinctKeys = list
.Select(pair => pair.Key)
.Distinct()
.OrderBy(pair => pair)
.ToArray();
var lastKeyIndex = -1;
for (var index = 0; index < list.Count; index++)
{
var currentKeyIndex = lastKeyIndex + 1 == distinctKeys.Length ? 0 : lastKeyIndex + 1;
var currentKey = distinctKeys[currentKeyIndex];
if (list[index].Key != currentKey)
{
list.Insert(index, new KeyValuePair<string, string>(currentKey, string.Empty));
}
lastKeyIndex = currentKeyIndex;
}
for (var index = lastKeyIndex+1; index < distinctKeys.Length; index++)
{
list.Add(new KeyValuePair<string, string>(distinctKeys[index], string.Empty));
}
但它没有给我预期的输出。
另一套尝试:
键:值
contacts.coid : 2003984
createdon : 2020-09-10
c_id : fcd5937d
contacts.coid : 2024489
createdon : 2020-09-10
contacts.fullname : Mark
contacts.coid : 99
c_id : 7e70096e
contacts.coid : 2024496
createdon : 2020-09-10
contacts.fullname : Simon
c_id : ebbbd1f4
预期输出
预期键:值
contacts.coid : 2003984
createdon : 2020-09-10
contacts.fullname : ""
c_id : fcd5937d
contacts.coid : 2024489
createdon : 2020-09-10
contacts.fullname : Mark
c_id : ""
contacts.coid : 99
createdon : ""
contacts.fullname : ""
c_id : 7e70096e
contacts.coid : 2024496
createdon : 2020-09-10
contacts.fullname : Simon
c_id : ebbbd1f4
欢迎任何想法来解决这个问题。
给定每个分组的第一个键,您可以对其进行分组,您可以创建一个完整的键列表,该列表按每个组的部分顺序排序,然后扩展每个组以获得完整的键集。
首先,IEnumerable
上的一些扩展可让您根据谓词进行分组(在每个分组为 true
时开始分组)和一个用于 DistinctBy
:
public static class IEnumerableExt {
// TRes seedFn(T FirstValue)
// TRes combineFn(TRes PrevResult, T CurValue)
// Based on APL scan operator
// Returns TRes
public static IEnumerable<TRes> Scan<T, TRes>(this IEnumerable<T> items, Func<T, TRes> seedFn, Func<TRes, T, TRes> combineFn) {
using (var itemsEnum = items.GetEnumerator()) {
if (itemsEnum.MoveNext()) {
var prev = seedFn(itemsEnum.Current);
while (itemsEnum.MoveNext()) {
yield return prev;
prev = combineFn(prev, itemsEnum.Current);
}
yield return prev;
}
}
}
// returns groups of T items each starting when testFn is true
public static IEnumerable<IEnumerable<T>> GroupByUntil<T>(this IEnumerable<T> items, Func<T, bool> testFn) =>
items.Scan(item => (groupNum: 0, theItem: item), (a, item) => testFn(item) ? (a.Item1+1, item) : (a.Item1, item))
.GroupBy(t => t.groupNum)
.Select(tg => tg.Select(t => t.theItem));
// returns a single item from each group of items by keyFn(item) picked by pickFn(itemGroup)
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> keyFn, Func<IGrouping<TKey, T>, T> pickFn, IEqualityComparer<TKey> comparer = null) =>
items.GroupBy(keyFn, comparer).Select(pickFn);
}
给定每组的第一个键:
var firstKey = "name";
您现在可以根据键在每组中出现的位置创建键的部分排序,然后对不同的键进行排序:
var ordering = list.GroupByUntil(kvp => kvp.Key == firstKey)
.OrderBy(g => g.Count())
.SelectMany((g,sn) => g.Select((g, n) => new { g.Key, n = (sn+1)*n }))
.OrderBy(kn => kn.n)
.DistinctBy(kn => kn.Key, g => g.Last())
.ToDictionary(kn => kn.Key, kn => kn.n);
var keySet = list.Select(kvp => kvp.Key).Distinct().OrderBy(k => ordering[k]).ToList();
使用 keySet
您可以展开每组项目以包含所有键:
var ans = list.GroupByUntil(kvp => kvp.Key == firstKey)
.Select(g => g.ToDictionary(l => l.Key, l => l.Value))
.SelectMany(d => keySet.Select(k => new KeyValuePair<string, string>(k, d.TryGetValue(k, out var v) ? v : "")));
如果您希望最终集仍然分组,只需将 SelectMany
替换为 Select
。
我已经做了很多挖掘工作,但似乎找不到这个特定问题的答案;类似问题的答案,但不是这样。
本质上,我要做的是在列表中添加具有默认值的缺失键。我有一个 List
键:值
name : Orange
actualname : Orango
name : Lime
fullname : Lime Lime
actualname : Limo
从上面的列表中,我想创建一个完整的集合,其中缺少密钥。
预期键:值
name : Orange
fullname : ""
actualname : Orango
name : Lime
fullname : Lime Lime
actualname : Limo
我正在尝试以下代码:
var list = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("name", "Orange"),
new KeyValuePair<string, string>("actualname", "Orango"),
new KeyValuePair<string, string>("name", "Lime"),
new KeyValuePair<string, string>("fullname", "Lime Lime"),
new KeyValuePair<string, string>("actualname", "Limo")
};
var distinctKeys = list
.Select(pair => pair.Key)
.Distinct()
.OrderBy(pair => pair)
.ToArray();
var lastKeyIndex = -1;
for (var index = 0; index < list.Count; index++)
{
var currentKeyIndex = lastKeyIndex + 1 == distinctKeys.Length ? 0 : lastKeyIndex + 1;
var currentKey = distinctKeys[currentKeyIndex];
if (list[index].Key != currentKey)
{
list.Insert(index, new KeyValuePair<string, string>(currentKey, string.Empty));
}
lastKeyIndex = currentKeyIndex;
}
for (var index = lastKeyIndex+1; index < distinctKeys.Length; index++)
{
list.Add(new KeyValuePair<string, string>(distinctKeys[index], string.Empty));
}
但它没有给我预期的输出。
另一套尝试:
键:值
contacts.coid : 2003984
createdon : 2020-09-10
c_id : fcd5937d
contacts.coid : 2024489
createdon : 2020-09-10
contacts.fullname : Mark
contacts.coid : 99
c_id : 7e70096e
contacts.coid : 2024496
createdon : 2020-09-10
contacts.fullname : Simon
c_id : ebbbd1f4
预期输出
预期键:值
contacts.coid : 2003984
createdon : 2020-09-10
contacts.fullname : ""
c_id : fcd5937d
contacts.coid : 2024489
createdon : 2020-09-10
contacts.fullname : Mark
c_id : ""
contacts.coid : 99
createdon : ""
contacts.fullname : ""
c_id : 7e70096e
contacts.coid : 2024496
createdon : 2020-09-10
contacts.fullname : Simon
c_id : ebbbd1f4
欢迎任何想法来解决这个问题。
给定每个分组的第一个键,您可以对其进行分组,您可以创建一个完整的键列表,该列表按每个组的部分顺序排序,然后扩展每个组以获得完整的键集。
首先,IEnumerable
上的一些扩展可让您根据谓词进行分组(在每个分组为 true
时开始分组)和一个用于 DistinctBy
:
public static class IEnumerableExt {
// TRes seedFn(T FirstValue)
// TRes combineFn(TRes PrevResult, T CurValue)
// Based on APL scan operator
// Returns TRes
public static IEnumerable<TRes> Scan<T, TRes>(this IEnumerable<T> items, Func<T, TRes> seedFn, Func<TRes, T, TRes> combineFn) {
using (var itemsEnum = items.GetEnumerator()) {
if (itemsEnum.MoveNext()) {
var prev = seedFn(itemsEnum.Current);
while (itemsEnum.MoveNext()) {
yield return prev;
prev = combineFn(prev, itemsEnum.Current);
}
yield return prev;
}
}
}
// returns groups of T items each starting when testFn is true
public static IEnumerable<IEnumerable<T>> GroupByUntil<T>(this IEnumerable<T> items, Func<T, bool> testFn) =>
items.Scan(item => (groupNum: 0, theItem: item), (a, item) => testFn(item) ? (a.Item1+1, item) : (a.Item1, item))
.GroupBy(t => t.groupNum)
.Select(tg => tg.Select(t => t.theItem));
// returns a single item from each group of items by keyFn(item) picked by pickFn(itemGroup)
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> keyFn, Func<IGrouping<TKey, T>, T> pickFn, IEqualityComparer<TKey> comparer = null) =>
items.GroupBy(keyFn, comparer).Select(pickFn);
}
给定每组的第一个键:
var firstKey = "name";
您现在可以根据键在每组中出现的位置创建键的部分排序,然后对不同的键进行排序:
var ordering = list.GroupByUntil(kvp => kvp.Key == firstKey)
.OrderBy(g => g.Count())
.SelectMany((g,sn) => g.Select((g, n) => new { g.Key, n = (sn+1)*n }))
.OrderBy(kn => kn.n)
.DistinctBy(kn => kn.Key, g => g.Last())
.ToDictionary(kn => kn.Key, kn => kn.n);
var keySet = list.Select(kvp => kvp.Key).Distinct().OrderBy(k => ordering[k]).ToList();
使用 keySet
您可以展开每组项目以包含所有键:
var ans = list.GroupByUntil(kvp => kvp.Key == firstKey)
.Select(g => g.ToDictionary(l => l.Key, l => l.Value))
.SelectMany(d => keySet.Select(k => new KeyValuePair<string, string>(k, d.TryGetValue(k, out var v) ? v : "")));
如果您希望最终集仍然分组,只需将 SelectMany
替换为 Select
。