using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace miew.Tally { [DebuggerDisplay("{ToString(),nq}")] public struct Tally<T> { public Tally(T item, int count) { this.Item = item; this.Count = count; } public readonly T Item; public int Count; public override string ToString() { return System.String.Format("{0} {1}", Item.ToString(), Count); } }; public interface ITallySet<T> : ICollection<T> { int GetTally(T item); }; [DebuggerDisplay("count={Count}")] public class TallySet<T> : ITallySet<T>, IEnumerable<Tally<T>> { [DebuggerBrowsable(DebuggerBrowsableState.Never)] Dictionary<T, int> tallies = new Dictionary<T, int>(); public bool Add(T item) { int i; if (tallies.TryGetValue(item, out i)) { tallies[item] = i + 1; return false; } tallies.Add(item, 1); return true; } public bool Remove(T item) { if (--tallies[item] > 0) return false; tallies.Remove(item); return true; } public int GetTally(T item) { return tallies[item]; } void ICollection<T>.Add(T item) { Add(item); } public void Clear() { tallies.Clear(); } public bool Contains(T item) { return tallies.ContainsKey(item); } public void CopyTo(T[] array, int arrayIndex) { tallies.Keys.CopyTo(array, arrayIndex); } public int Count { get { return tallies.Count; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsReadOnly { get { return false; } } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return tallies.Keys.GetEnumerator(); } IEnumerator<Tally<T>> IEnumerable<Tally<T>>.GetEnumerator() { foreach (var kvp in tallies) yield return new Tally<T>(kvp.Key, kvp.Value); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return ((IEnumerable<Tally<T>>)this).GetEnumerator(); } #if DEBUG [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public Tally<T>[] _items { get { return ((IEnumerable<Tally<T>>)this).ToArray(); } } #endif }; public static class Ext { /// <summary> /// Determine tallies for each distinct group of source elements /// </summary> public static IEnumerable<Tally<TSrc>> ToTallies<TSrc>(this IEnumerable<TSrc> seq) { Dictionary<TSrc, int> d = new Dictionary<TSrc, int>(); foreach (TSrc t in seq) { if (d.ContainsKey(t)) d[t]++; else d.Add(t, 1); } foreach (var kvp in d) yield return new Tally<TSrc>(kvp.Key, kvp.Value); } /// <summary> /// Determine tallies for each distinct group of source elements /// </summary> public static IEnumerable<Tally<TSrc>> ToTallies<TSrc>(this IEnumerable<TSrc> seq, IEqualityComparer<TSrc> c) { Dictionary<TSrc, int> d = new Dictionary<TSrc, int>(c); foreach (TSrc t in seq) { if (d.ContainsKey(t)) d[t]++; else d.Add(t, 1); } foreach (var kvp in d) yield return new Tally<TSrc>(kvp.Key, kvp.Value); } /// <summary> /// Return tallies for the distinct groups of source elements, determined according to the key selector. Each group is /// identified by (an arbitrary) one of its source elements /// </summary> public static IEnumerable<Tally<TSrc>> ToTallies<TSrc, TKey>(this IEnumerable<TSrc> ie, Func<TSrc, TKey> fn, IEqualityComparer<TKey> c) { Dictionary<TKey, Tally<TSrc>> d = new Dictionary<TKey, Tally<TSrc>>(c); foreach (TSrc t in ie) { Tally<TSrc> tal; TKey k = fn(t); if (d.TryGetValue(k, out tal)) d[k] = new Tally<TSrc>(t, tal.Count + 1); else d.Add(k, new Tally<TSrc>(t, 1)); } return d.Values; } /// <summary> /// Determine tallies for each distinct group of dictionary values /// </summary> public static IEnumerable<Tally<V>> TallyValues<K, V>(this IDictionary<K, V> dict) { Dictionary<V, int> td = new Dictionary<V, int>(); foreach (var kvp in dict) { if (td.ContainsKey(kvp.Value)) td[kvp.Value]++; else td.Add(kvp.Value, 1); } foreach (var kvp in td) yield return new Tally<V>(kvp.Key, kvp.Value); } /// <summary> /// Determine tallies for each distinct group of dictionary values /// </summary> public static IEnumerable<Tally<V>> TallyValues<K, V>(this IDictionary<K, V> dict, IEqualityComparer<V> c) { Dictionary<V, int> td = new Dictionary<V, int>(c); foreach (var kvp in dict) { if (td.ContainsKey(kvp.Value)) td[kvp.Value]++; else td.Add(kvp.Value, 1); } foreach (var kvp in td) yield return new Tally<V>(kvp.Key, kvp.Value); } }; }