Skip to content

Instantly share code, notes, and snippets.

@Emzi0767
Created September 22, 2020 16:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Emzi0767/0211e43e107d0a7ab3221813572ce86e to your computer and use it in GitHub Desktop.
Save Emzi0767/0211e43e107d0a7ab3221813572ce86e to your computer and use it in GitHub Desktop.
An elaborate shitpost. Transforms TKey-indexed dictionaries into T-indexed lookups.
// Copyright 2020 Emzi0767
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ScratchPad
{
public static class Program
{
public static void Main(string[] args)
{
var dict = new Dictionary<ulong, Something>(3)
{
[1_000_000_000_000UL] = new Something(1_000_000_000_000UL, "Asdf", 42),
[2_000_000_000_000UL] = new Something(2_000_000_000_000UL, "Fdsa", 69),
[3_000_000_000_000UL] = new Something(3_000_000_000_000UL, "ur mum", 18)
};
var qw = dict.CreateQueryWrapper(x => x.Name, StringComparer.OrdinalIgnoreCase);
var s = qw["fdsa"];
Console.WriteLine(s);
}
public sealed class Something
{
public ulong Id { get; }
public string Name { get; }
public int Age { get; }
public Something(ulong id, string name, int age)
{
this.Id = id;
this.Name = name;
this.Age = age;
}
public override string ToString()
=> $"{this.Id}: {this.Name}/{this.Age}";
}
public static IReadOnlyDictionary<TProp, T> CreateQueryWrapper<T, TProp, TKey>(
this IReadOnlyDictionary<TKey, T> dict,
Expression<Func<T, TProp>> querySubject,
IEqualityComparer<TProp> equalityComparer = default)
{
if (!(querySubject.Body is MemberExpression member) || !(member.Member is PropertyInfo prop))
throw new ArgumentException("Must be a property expression.", nameof(querySubject));
var gm = prop.GetGetMethod();
if (gm is null)
throw new ArgumentException("Must be a readable property.", nameof(querySubject));
var getter = gm.CreateDelegate(typeof(Func<T, TProp>)) as Func<T, TProp>;
return new QueryWrapper<T, TProp>(dict.Values, getter, equalityComparer);
}
private class QueryWrapper<T, TProp> : IReadOnlyDictionary<TProp, T>
{
private IEnumerable<T> Items { get; }
private Func<T, TProp> Getter { get; }
private IEqualityComparer<TProp> EqualityComparer { get; }
public IEnumerable<TProp> Keys
=> this.Items.Select(this.Getter).Distinct(this.EqualityComparer);
public IEnumerable<T> Values
=> this.Items;
public int Count
=> this.Items.Count();
public T this[TProp key]
{
get => this.Items.Single(x => this.EqualityComparer.Equals(this.Getter(x), key));
}
public QueryWrapper(
IEnumerable<T> items,
Func<T, TProp> getter,
IEqualityComparer<TProp> equalityComparer)
{
this.Items = items;
this.Getter = getter;
this.EqualityComparer = equalityComparer ?? EqualityComparer<TProp>.Default;
}
public bool ContainsKey(TProp key)
=> this.Items.Any(x => this.EqualityComparer.Equals(this.Getter(x), key));
public bool TryGetValue(TProp key, out T value)
{
value = default;
if (!this.ContainsKey(key))
return false;
value = this[key];
return true;
}
public IEnumerator<KeyValuePair<TProp, T>> GetEnumerator()
=> this.Items.Select(x => new KeyValuePair<TProp, T>(this.Getter(x), x)).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> this.GetEnumerator();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment