using System.Diagnostics.CodeAnalysis;
using BuildingDirectory.Model;
namespace BuildingDirectory;
///
/// A generic dictionary (hash map) implementation managing keys and associated values
///
/// Type of the keys; cannot be null
/// Type of the values
public sealed class MyDictionary where TKey : notnull
{
private const int InitialBuckets = 4;
private const int MaxDepth = 2;
private List[] _buckets;
private int _currentMaxDepth;
///
/// Gets the count of items stored in this dictionary
///
public int Count { get; private set; }
private int NoOfBuckets => _buckets.Length;
///
/// Creates a new instance of the dictionary with default capacity
///
public MyDictionary() : this(InitialBuckets)
{
}
private MyDictionary(int capacity)
{
_buckets = CreateBuckets(capacity);
// idk, guess I won't use it
_currentMaxDepth = capacity;
}
///
/// Attempts to get the value associated with the given key.
/// Returns the default value for if not found.
///
/// Key of the required value
public TValue? this[TKey key]
{
get
{
TryGetValue(key, out TValue? value);
return value ?? default;
}
}
///
/// Returns a list of all managed keys.
///
/// A list of all keys
public List GetKeys()
{
var keysAndValues = GetKeysAndValues();
List keys = [];
foreach (var keyValue in keysAndValues)
{
keys.Add(keyValue.Key);
}
return keys;
}
///
/// Returns a list of all managed values.
/// May contain duplicates.
///
/// A list of all values
public List GetValues()
{
var keysAndValues = GetKeysAndValues();
List values = [];
foreach (var keyValue in keysAndValues)
{
values.Add(keyValue.Value);
}
return values;
}
///
/// Adds a value to the dictionary by its key.
/// If the key is already present the previous values is replaced.
///
/// Key of the value
/// Value to store
public void Add(TKey key, TValue value)
{
var bucketIdx = GetBucketIndex(key, NoOfBuckets);
// Will always be false, but anyway. Its here and the tests are green :)
if (bucketIdx >= _buckets.Length)
{
Grow();
}
if (ContainsKey(key))
{
Remove(key);
}
_buckets[bucketIdx].Add(new KeyValue(key, value));
Count++;
}
///
/// Attempts to remove the given key and associated value from the dictionary.
/// Returns false if key is not found.
///
/// Key to remove
/// True if the key & value could be removed; false otherwise
public bool Remove(TKey key)
{
var bucketIdx = GetBucketIndex(key, NoOfBuckets);
for (var i = 0; i < _buckets[bucketIdx].Count; i++)
{
if (_buckets[bucketIdx][i].Key.Equals(key))
{
_buckets[bucketIdx].RemoveAt(i);
Count--;
return true;
}
}
return false;
}
///
/// Attempts to get a value by the given key from the dictionary.
/// If not found value is set to the default of .
///
/// Key to look for
/// Out param for storing value if found
/// True if key was found; false otherwise
public bool TryGetValue(TKey key, out TValue? value)
{
value = default;
var found = TryGetValue(key, out KeyValue? keyValue);
if (keyValue == null)
{
return false;
}
value = keyValue.Value;
return found;
}
///
/// Checks if the dictionary contains the given key
///
/// Key to search for
/// True if key is found; false otherwise
public bool ContainsKey(TKey key)
{
var bucketIdx = GetBucketIndex(key, NoOfBuckets);
foreach (var keyValue in _buckets[bucketIdx])
{
if (keyValue.Key.Equals(key))
{
return true;
}
}
return false;
}
private bool TryGetValue(TKey key, out KeyValue? existingKeyValue)
{
existingKeyValue = null;
var bucketIdx = GetBucketIndex(key, NoOfBuckets);
foreach (var keyValue in _buckets[bucketIdx])
{
if (keyValue.Key.Equals(key))
{
existingKeyValue = keyValue;
return true;
}
}
return existingKeyValue != null;
}
private List GetKeysAndValues()
{
List list = [];
foreach (var bucket in _buckets)
{
list.AddRange(bucket);
}
return list;
}
private void Grow()
{
var newBuckets = CreateBuckets(_buckets.Length * 2);
for (var i = 0; i < _buckets.Length; i++)
{
for (var j = 0; j < _buckets[i].Count; j++)
{
int newIndex = GetBucketIndex(_buckets[i][j].Key, newBuckets.Length);
newBuckets[newIndex].Add(_buckets[i][j]);
}
}
_buckets = newBuckets;
}
private static List[] CreateBuckets(int amount)
{
var buckets = new List[amount];
for (var i = 0; i < amount; i++)
{
buckets[i] = new List();
}
return buckets;
}
private int GetBucketIndex(TKey key, int? bucketCount = null)
{
var count = bucketCount ?? _buckets.Length;
return Math.Abs(key.GetHashCode() % count);
}
// Object where the key-value pairs are stored
private sealed class KeyValue
{
public KeyValue(TKey key, TValue value)
{
Key = key;
Value = value;
}
public TKey Key { get; }
public TValue Value { get; }
private bool Equals(KeyValue other)
{
if (Value == null || other.Value == null)
{
return false;
}
return Key.Equals(other.Key) && Value.Equals(other.Value) && this.GetHashCode() == other.GetHashCode();
}
public override bool Equals(object? obj)
{
var other = obj as KeyValue;
if (obj != other)
{
return false;
}
return other != null && Equals(other);
}
public override int GetHashCode()
{
if (Value == null)
{
return Key.GetHashCode();
}
return Value.GetHashCode();
}
}
}