我想编写一个 linq 扩展(或自定义字典、排序列表列表或任何最好的解决方案),它允许我向集合添加一个值,其键是“下一个可用”。
例如:
int CreatedKey = IncrementDictionary.AddNext(myCustomer);
如果当前存在的key如下:
1
2
8
4
3
然后它将 myCustomer 添加到具有 5 键的字典中,并返回该键。
你怎么看?
我想编写一个 linq 扩展(或自定义字典、排序列表列表或任何最好的解决方案),它允许我向集合添加一个值,其键是“下一个可用”。
例如:
int CreatedKey = IncrementDictionary.AddNext(myCustomer);
如果当前存在的key如下:
1
2
8
4
3
然后它将 myCustomer 添加到具有 5 键的字典中,并返回该键。
你怎么看?
public static int AddNext(this Dictionary<int, string> dict)
{
int min = dict.Keys.Min();
int max = dict.Keys.Max();
return Enumerable.Range(min, max-min).Except(dict.Keys).First();
}
用它作为
int result = new Dictionary<int, string>(){ {1, "a"}, {2, "a"},
{8, "a"}, {4, "a"},
{3, "a"}}.AddNext();
这里result
是5。
您可以使用带有 Extension 方法的SortedList来添加到下一个自动检索的键。
假设数据结构是任何对象,带有数字键,
以下是 SortedList 的 ExtensionMethod
public static class SortedListExtensions
{
///Add item to sortedList (numeric key) to next available key item, and return key
public static int AddNext<T>(this SortedList<int, T> sortedList, T item)
{
int key = 1; // Make it 0 to start from Zero based index
int count = sortedList.Count;
int counter=0;
do
{
if (count == 0) break;
int nextKeyInList = sortedList.Keys[counter++];
if (key != nextKeyInList) break;
key = nextKeyInList +1;
if (count == 1 || counter == count ) break;
if (key != sortedList.Keys[counter])
break;
} while (true);
sortedList.Add(key, item);
return key;
}
}
它可以像下面这样使用
SortedList<int, string> x = new SortedList<int, string>();
x.Add(4, "BCD");
x.Add(6, "BCD");
x.AddNext("a");
x.AddNext("b");
x.AddNext("c");
x.AddNext("d");
x.AddNext("e");
foreach (var item in x)
Console.WriteLine(item.Key + " " + item.Value);
输出是
1 a
2 b
3 c
4 BCD
5 d
6 BCD
7 e
您可以使用字典或任何其他数据结构。在这种情况下,将需要双循环。在 SortedList 的情况下,搜索键时会保存一个循环。SortedList.Add
此循环由使用 BinarySearch 算法的函数在内部使用。
二进制搜索比循环所有元素(对于更大尺寸的数据)更快。
这是我的解决方案比第一个更短,但您可以从中有所了解。
Dictionary<int, string> x = new Dictionary<int, string>();
x.Add(1, "a");
x.Add(2, "a");
x.Add(3, "a");
x.Add(4, "a");
x.Add(8, "a");
Console.WriteLine((x.Keys.Where(k => !x.Keys.Contains(k + 1)).Min() + 1).ToString());
您是否正在寻找这样的东西(它显然只能包含 1000 个元素)?可能有许多其他解决方案,但很难说出您到底想做什么。无论如何,这可能是一个起点。
public class IncrementDictionary : Dictionary<int, object>
{
private bool[] usedKeys = new bool[1000];
public new void Add(int key, object value)
{
base.Add(key, value);
usedKeys[key] = true;
}
public new void Clear()
{
base.Clear();
usedKeys = new bool[1000];
}
public new object this[int key]
{
get
{
return base[key];
}
set
{
base[key] = value;
usedKeys[key] = true;
}
}
public new bool Remove(int key)
{
usedKeys[key] = false;
return base.Remove(key);
}
public int AddNext(object anObj)
{
int newKey = -1;
for (int i = 1; i < 1000; i++)
if (!usedKeys[i])
{
newKey = i;
break;
}
if (newKey > 0)
this.Add(newKey, anObj);
return newKey;
}
}
嗯......这是一个老问题,但我想回答。
Nikhil Agrawal 的代码可能会有所改进,考虑到如果您有一个空集合,您必须指定最小值是多少。所以代码变成了:
public static int FirstFree(Dictionary<int, Guid> dict, int minumum)
{
int min = dict.Count == 0
? minumum //use passed minimum if needed
: dict.Keys.Min(); //use real minimum
int max = dict.Count > 1
? dict.Keys.Max() + 2 //two steps away from maximum, avoids exceptions
: min + 2; //emulate data presence
return Enumerable.Range(min, max).Except(dict.Keys).First();
}
但是,如果您在定义的范围内工作,您还应该知道是否有空间来存储值。这可以是这样的:
public static int? FirstFree(Dictionary<int, Guid> dict, int min, int max)
{
if (max <= min)
throw new Exception($"Specified range is invalid (must be max > min)");
if (max - min + 1 == dict.Count)
//no space left
return null;
return Enumerable.Range(min, max).Except(dict.Keys).First();
}
如果你不喜欢 nullable-int,你可以检查 result 是否是允许的最小值。显然,如果您指定 min=int.MinValue,我知道它不起作用,但在这种情况下,问题是此方法创建的巨大集合!
public static int FirstFree(Dictionary<int, Guid> dict, int min, int max)
{
if (max <= min)
throw new Exception($"Specified range is invalid (must be max > min)");
if (max - min + 1 == dict.Count)
//no space left
return int.MinValue;
return Enumerable.Range(min, max).Except(dict.Keys).First();
}