How to create a sorting index in C#? - c#

I have an array of values and I want to create a sorting index, i.e. an auxiliary array of integers that lists the element in sorted order by indirect addressing.
In other words,
I <= J -> Value[Index[I]] <= Value[Index[J]]
How can I define a comparator for the Sort method to achieve that ? The array of values must remain unchanged.

The easiest way to build such an index I see is to use LINQ:
var Index = Enumerable.Range(0, Value.Length).OrderBy(i => Value[i]).ToArray();
or if you insist on using Array.Sort, then you can use the overloads that accept Comparison<T> delegate:
var Index = new int[Value.Length];
for (int i = 0; i < Index.Length; i++)
Index[i] = i;
Array.Sort(Index, (a, b) => Comparer<Value_Type>.Default.Compare(Value[a], Value[b]));
where the Value_Type is the type of the Value array elements.
Another option (IMO the best) is to create a reusable generic comparer like this:
public static class Comparers
{
public static IComparer<int> CreateIndexComparer<T>(this IReadOnlyList<T> source, IComparer<T> comparer = null)
{
return new ListIndexComparer<T>(source, comparer);
}
private sealed class ListIndexComparer<T> : Comparer<int>
{
readonly IReadOnlyList<T> list;
readonly IComparer<T> comparer;
public ListIndexComparer(IReadOnlyList<T> list, IComparer<T> comparer = null)
{
this.list = list;
this.comparer = comparer ?? Comparer<T>.Default;
}
public override int Compare(int x, int y)
{
return x != y ? comparer.Compare(list[x], list[y]) : 0;
}
}
}
and use it with the Array.Sort overloads that accept IComparer<T>:
Array.Sort(Index, Value.CreateIndexComparer());

Related

c# extension methods for generic array

I'm trying to make extension methods for generic array, so I could takeout random set of elements.
I made following extension methods for List<T> type and they work great, but I can't work out how to do exactly the same for generic array:
public static T Random<T>(this List<T> list)
{
return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}
public static IEquatable Random<IEquatable>(this List<IEquatable> list, List<IEquatable> hits)
{
int rand = GameManager.instance.functions.RandomInt(list.Count - 1);
while (hits.Exists(h => h.Equals(list[rand])))
rand = GameManager.instance.functions.RandomInt(list.Count - 1);
return list[rand];
}
public static List<T> Random<T>(this List<T> list, int count)
{
List<T> result = new List<T>();
for (int i = 0; i < count; i++)
{
result.Add(list.Random());
}
return result;
}
public static List<IEquatable> RandomUnique<IEquatable>(this List<IEquatable> list, int count)
{
List<IEquatable> result = new List<IEquatable>();
for (int i = 0; i < count; i++)
{
result.Add(list.Random(result));
}
return result;
}
I tried to rework the first method like this:
public static IEnumerable Random<IEnumerable>(this IEnumerable list)
but it doesn't recognize list as an array so I can't get to it's length value.
I see a workaround, to do a List from Array, then get my random values and make array again, but it's seems like too much action for just taking eg. 2 random from 4 elements array.
Please advise
EDIT:
Thanks to Mathew in comments, I managed to construct the extension method for generic array correctly:
public static T Random<T>(this T[] list)
{
return list[GameManager.instance.functions.RandomInt(list.Length - 1)];
}
But ultimately I'll play around with the Dmitry's answer and try to make these for IEnumerable. Thank you very much!
EDIT2:
Thanks to Zastai, I changed all methods so they work for both List and generic array:
public static T Random<T>(this IReadOnlyList<T> list)
{
return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}
public static IEquatable Random<IEquatable>(this IReadOnlyList<IEquatable> list, List<IEquatable> hits)
{
int rand = GameManager.instance.functions.RandomInt(list.Count - 1);
while (hits.Exists(h => h.Equals(list[rand])))
rand = GameManager.instance.functions.RandomInt(list.Count - 1);
return list[rand];
}
public static List<T> Random<T>(this IReadOnlyList<T> list, int count)
{
List<T> result = new();
for (int i = 0; i < count; i++)
{
result.Add(list.Random());
}
return result;
}
public static List<IEquatable> RandomUnique<IEquatable>(this IReadOnlyList<IEquatable> list, int count)
{
List<IEquatable> result = new();
for (int i = 0; i < count; i++)
{
result.Add(list.Random(result));
}
return result;
}
Doesn't work for strings (as in "abcdefg".Random()), but for my needs it's not neccessary.
IEnumerable is specifically just a sequence of values, and has no length.
IReadOnlyList on the other hand, is a list of values (so does have a length) and does not allow adding/removing values.
A .NET array implements both.
So if you change your extension methods to take IReadOnlyList<xxx> instead of List<xxx> they should automatically work on arrays too.
Instead of implementing extensions methods for List<T>, T[] etc. you can try implementing a
single routine for IEnumerable<T>, e.g.
public static partial class EnumerableExtensions {
public static T Random<T>(this IEnumerable<T> source) {
//DONE: do not forget to validate public methods' arguments
if (source is null)
throw new ArgumentNullException(nameof(source));
// If enumerable is a collection (array, list) we can address items explictitly
if (source is ICollection<T> collection) {
if (collection.Count <= 0)
throw new ArgumentOutOfRangeException(nameof(source),
$"Empty {nameof(source)} is not supported.");
return collection[GameManager.instance.functions.RandomInt(collection.Count - 1)];
}
// In general case we have to materialize the enumeration
var list = source.ToList();
if (list.Count <= 0)
throw new ArgumentOutOfRangeException(nameof(source),
$"Empty {nameof(source)} is not supported.");
return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}
}
Then you can use the same extension method with list, array etc.:
// Array
int demo1 = new int[] {4, 5, 6}.Random();
// List
double demo2 = new List<double>() {1.0. 3.0}.Random();
// String is not array or list but implements IEnumerable<char>
char demo3 = "abcdef".Random();
As an alternative to consider: You can use Reservoir sampling to select N items from a sequence of unknown length.
Here's a sample implementation:
/// <summary>Randomly selects n elements from a sequence of items.</summary>
public static List<T> RandomlySelectedItems<T>(IEnumerable<T> items, int n, System.Random rng)
{
// See http://en.wikipedia.org/wiki/Reservoir_sampling for details.
var result = new List<T>(n);
int index = 0;
foreach (var item in items)
{
if (index < n)
{
result.Add(item);
}
else
{
int r = rng.Next(0, index + 1);
if (r < n)
result[r] = item;
}
++index;
}
if (index < n)
throw new ArgumentException("Input sequence too short");
return result;
}

How to get a distinct result for list of array?

I have a list of long type array.
List<ulong[]> TestList = new List<ulong[]>();
and list has following items.
{1,2,3,4,5,6},
{2,3,4,5,6,7},
{3,4,5,6,7,8},
{1,2,3,4,5,6}
and expected distinct result is
{1,2,3,4,5,6},
{2,3,4,5,6,7},
{3,4,5,6,7,8}
So I try as following, but useless.
TestList = TestList.Distinct().ToList();
Am I need something special comparer for getting distinct list?
Distinct() uses the default equality check, which for arrays is reference equality. It does not check the contents of the array for equality.
If you want to do that, you'll need the overload of Distinct() that takes an IEqualityComparer<T>. This allows you to customize the behaviour to determine if two items are equal or not.
For comparing arrays, IStructuralEquatable and friends already do the heavy lifting. You can wrap it simply, like so:
sealed class StructuralComparer<T> : IEqualityComparer<T>
{
public static IEqualityComparer<T> Instance { get; } = new StructuralComparer<T>();
public bool Equals(T x, T y)
=> StructuralComparisons.StructuralEqualityComparer.Equals(x, y);
public int GetHashCode(T obj)
=> StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
}
Then, use it in the Distinct() call like this:
TestList = TestList.Distinct(StructuralComparer<ulong[]>.Instance).ToList();
You need to provide an equality comparer, default implementation does not know how to compare arrays of long (it uses reference equality):
class LongArrayComparer : EqualityComparer<long[]>
{
public override bool Equals(long[] a1, long[] a2)
{
if (a1 == null && a2 == null)
return true;
else if (a1 == null || a2 == null)
return false;
return a1.SequenceEqual(a2);
}
public override int GetHashCode(long[] arr)
{
long hCode = arr.Aggregate(0, (acc, it) => acc ^ it);
return hCode.GetHashCode();
}
}
Then use it:
TestList = TestList.Distinct(new LongArrayComparer()).ToList();
List<ulong[]> TestList = new List<ulong[]>() {
new ulong[]{ 1,2,3,4,5,6},
new ulong[]{ 2,3,4,5,6,7},
new ulong[]{ 3,4,5,6,7,8},
new ulong[]{ 1,2,3,4,5,6}
};
var result = TestList.GroupBy(x => String.Join(",", x))
.Select(x => x.First().ToArray())
.ToList();
You can implement an IEqualityComparer
public class IntArrayComparer : IEqualityComparer<string[]>
{
public bool Equals(int[] x, int[] y)
{
var shared = x.Intersect(y);
return x.Length == y.Length && shared.Count() == x.Length;;
}
public int GetHashCode(int[] obj)
{
int hashCode=obj.Length;
for(int i=0;i<obj.Length;++i)
{
hashCode=unchecked(hashCode*314159 +obj[i]);
}
return hashCode;
}
}
Then can implement it:
TestList = TestList.Distinct(new IntArrayComparer()).ToList();

C# Enumerable.Take with default value

What is the best way to get exactly x values from an Enumerable in C#.
If i use Enumerable .Take() like this:
var myList = Enumerable.Range(0,10);
var result = myList.Take(20);
The result will only have 10 elements.
I want to fill the missing entries with a default value.
Something like this:
var myList = Enumerable.Range(0,10);
var result = myList.TakeOrDefault(20, default(int)); //Is there anything like this?
Is there such a function in C# and if not, what would be the best way to achieve this?
You could do something like:
var result = myList.Concat(Enumerable.Repeat(default(int), 20)).Take(20);
And it would be easy to turn this into an extension method:
public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int count, T defaultValue)
{
return list.Concat(Enumerable.Repeat(defaultValue, count)).Take(count);
}
But there is a subtle gotcha here. This would work perfectly fine for value types, for a reference type, if your defaultValue isn't null, you are adding the same object multiple times. Which probably isn't want you want. For example, if you had this:
var result = myList.TakeOrDefault(20, new Foo());
You are going to add the same instance of Foo to pad your collection. To solve that problem, you'd need something like this:
public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int count, Func<T> defaultFactory)
{
return list.Concat(Enumerable.Range(0, count).Select(i => defaultFactory())).Take(count);
}
Which you'd call like this:
var result = myList.TakeOrDefault(20, () => new Foo())
Of course, both methods can co-exist, so you could easily have:
// pad a list of ints with zeroes
var intResult = myIntList.TakeOrDefault(20, default(int));
// pad a list of objects with null
var objNullResult = myObjList.TakeOrDefault(20, (object)null);
// pad a list of Foo with new (separate) instances of Foo
var objPadNewResult = myFooList.TakeOrDefault(20, () => new Foo());
Its not there by default, but it's easy enough to write as an extension method
public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> items, int count, T defaultValue)
{
var i = 0;
foreach(var item in items)
{
i++;
yield return item;
if(i == count)
yield break;
}
while(i++<count)
{
yield return defaultValue;
}
}
Live example: http://rextester.com/XANF91263
What you're looking for is a general-purpose PadTo method, which extends the collection's length if needed using a given value.
public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len)
{
return source.PadTo(len, default(T));
}
public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len, T elem)
{
return source.PadTo(len, () => elem);
}
public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len, Func<T> elem)
{
int i = 0;
foreach(var t in source)
{
i++;
yield return t;
}
while(i++ < len)
yield return elem();
}
You can now express:
myList.Take(20).PadTo(20);
This is analogous to Scala's List[A].padTo
You could use Concat for this purpose. You can use a simple helper method to join this all together:
public IEnumerable<T> TakeSpawn(this IEnumerable<T> #this, int take, T defaultElement)
{
return #this.Concat(Enumerable.Repeat(defaultElement, take)).Take(take);
}
The idea is that you always append another enumerable on the end of the original enumerable, so if the input doesn't have enough elements, it will start enumerating from the Repeat.
There isn't anything in the .NET Framework, not that I'm aware of. This can be achieved easily using an extension method though and it works for all types if you supply a default value yourself:
public static class ListExtensions
{
public static IEnumerable<T> TakeOrDefault<T>(this List<T> list, int count, T defaultValue)
{
int missingItems = count - list.Count;
List<T> extra = new List<T>(missingItems);
for (int i = 0; i < missingItems; i++)
extra.Add(defaultValue);
return list.Take(count).Concat(extra);
}
}
I wrote a quick extension for this which depends on T being a value type.
public static class Extensions
{
public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int totalElements)
{
List<T> finalList = list.ToList();
if (list.Count() < totalElements)
{
for (int i = list.Count(); i < totalElements; i++)
{
finalList.Add(Activator.CreateInstance<T>());
}
}
return finalList;
}
}
Why not just write an extension method that checks the count and returns the default value for remaining entries:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
List<int> values = new List<int>{1, 2, 3, 4};
IEnumerable<int> moreValues = values.TakeOrDefault(3, 100);
Console.WriteLine(moreValues.Count());
moreValues = values.TakeOrDefault(4, 100);
Console.WriteLine(moreValues.Count());
moreValues = values.TakeOrDefault(10, 100);
Console.WriteLine(moreValues.Count());
}
}
public static class ExtensionMethods
{
public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> enumerable, int count, T defaultValue)
{
int returnedCount = 0;
foreach (T variable in enumerable)
{
returnedCount++;
yield return variable;
if (returnedCount == count)
{
yield break;
}
}
if (returnedCount < count)
{
for (int i = returnedCount; i < count; i++)
{
yield return defaultValue;
}
}
}
}
}

IComparer not working as expected

My program's function is to count the occurrences of unique words in a document, and then display them in sorted order. I first loop through all the words and enter them into a dictionary, and increment the value in the dictionary for how many times they have been encountered. I then convert the dictionary to a list and call the .Sort method with an IComparer as a parameter. Shown in this code here:
List<KeyValuePair<string,long>> wordList = wordCount.ToList();
IComparer<KeyValuePair<string,long>> comparison = new comparator();
wordList.Sort(comparison);
And the IComparer class I am using
public class comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
if (x.Value > y.Value)
return 1;
else
return 0;
}
}
However, when I am finished with the sorting, the list is not ordered by the value of the KeyValuePair like as I hoped it would be. What am I doing wrong here?
You're missing the case when y.Value is greater than x.Value in your comparer implementation:
public class comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
if (x.Value > y.Value)
{
return 1;
}
else if (x.Value < y.Value)
{
return -1;
}
else
return 0;
}
}
or
public class comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
return x.Value.CompareTo(y.Value);
}
}
You could also use LINQ OrderBy instead of Sort. It's easier to use because it takes a lambda expression, but it will create a new collection, instead of sorting the provided one.
var sorted = wordList.OrderByDescending(x => x.Value).ToList();
You could do all your processing in one query (assuming words is a collection of strings with all the words):
var sortedWithCount = words.GroupBy(x => x)
.OrderByDescending(g => g.Count)
.ToList(g => new { Word = g.Key, Count = g.Count });
Actually, you should return 1, 0 and -1 as a result fo the Compare method. But in your case, you could just use the CompareTo method from long type, for sample:
public class Comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
return x.Value.CompareTo(y.Value);
}
}
As a good pratice, rename your class to Comparator and not comparator. Keep the clean code on!

Dictionary<int [], bool> - compare values in the array, not reference?

I am using dictionary for storing ID,otherID and bool value. Unfortunately it compares array reference, therefore I cannot use it.
Is there any way how to have an array as key but compare its values instead of reference?
Thanks
You can use the Comparer property of the dictionary to set it to a custom comparer created by you.
EDIT: actually the property is read-only, sorry. You should definitely use the proper constructor:
class IntArrayComparer : IEqualityComparer<int[]> {
public bool Equals(int[] x, int[] y) {
if (x.Length != y.Length) {
return false;
}
for (int i = 0; i < x.Length; ++i) {
if (x[i] != y[i]) {
return false;
}
}
return true;
}
public int GetHashCode(int[] obj) {
int ret = 0;
for (int i = 0; i < obj.Length; ++i) {
ret ^= obj[i].GetHashCode();
}
return ret;
}
}
static void Main(string[] args) {
Dictionary<int[], bool> dict = new Dictionary<int[], bool>(new IntArrayComparer());
}
You can try implementing IEqualityComparer<int[]> and then pass an instance of it to the proper constructor.
There are basically two ways of doing that:
Create a comparer that implements IEqualityComparable<int[]>, that you pass to the constructor of the dictionary.
Create a key class that encapsulates the integer array and implements IEquatable<T>.
There's nothing wrong with orsogufo's answer, but I wanted to point out that if you have .NET 3.5, you can implement an ArrayValueComparer with a lot less code, and at the same time make it generic, so it can compare the values of any type of array, and not just integer arrays. For that matter, you could easily make it work with any IEnumerable, and not just arrays.
using System.Collections.Generic;
using System.Linq;
class ArrayValueComparer<T> : IEqualityComparer<T[]>
{
public bool Equals(T[] x, T[] y)
{
return x.SequenceEqual(y, EqualityComparer<T>.Default);
}
public int GetHashCode(T[] obj)
{
return obj.Aggregate(0, (total, next) => total ^ next.GetHashCode());
}
}
static void Main(string[] args)
{
var dict = new Dictionary<int[], bool>(new ArrayValueComparer<int>());
}

Categories

Resources