Using HashSet.orderBy() function - c#

Trying to sort:
HashSet<int> objHash = new HashSet<int>();
objHash.Add(14);
objHash.Add(12);
objHash.Add(11);
objHash.Add(13);
HashSet contians method OrderBy() with definition:
IOrderEnumerable<int> IEnumerable<int>.OrderBy<int,TKey>(Func<int,TKey>keySelector)
Trying to understand description:
IOrderEnumerable<int>- function OrderBy() returns
IEnumerable<int> - extension method defined in IEnumerable<int>. Can I somehow see body of this method?
.OrderBy<int,TKey>(Func<int,TKey>keySelector) - keySelector is function delegate with parameter of type int that returns TKey
Please, correct where my description interpretation is wrong.
How to use this function to order items in objHash according custom defined rules?

You are reading the quick info on the function correctly. To sort by your custom logic, you need to define what will be the return type as Tkey and pass the function that matches the delegate. The order by will then sort based on the returned value.
public static void Main()
{
var h = new HashSet<int> () {1, 2, 3, 4, 5};
var d = h.OrderBy<int, int>(x => MyFunc(x));
foreach(var a in d)
Console.WriteLine(a); //will order based on the returned value of MyFunc(x). in descending order.
// output will be 5 4 3 2 1 instead of 1 2 3 4 5.
}
public static int MyFunc(int x)
{
return x * -1;
}
And of course, if your Tkey is an object, you should want to implement IComparable to tell how to sort that object.

The OrderBy() method accepts the elements of your collection of objects (in your case, it is an int collection) and it should return a key for each specific object in the collection. Those returned keys are what will be used to sort the collection. Notice that these returned keys should be comparable between each other, so that the sorting can be performed.
Let's say you want to sort your numbers, while separating even numbers from odd numbers. A naive way of doing this would be making OrderBy() return string-typed keys, while placing a "0" character prefix on even numbers and a "1" character prefix on odd numbers, like that:
Number Returned key
14 "014"
12 "012"
11 "111"
13 "113"
Then you can do it like:
var ordered = objHash.OrderBy(n => $"{n % 2}{n}");
foreach (var x in ordered)
Console.WriteLine($" {x}");
This would output numbers in the following order: 12, 14, 11, 13 (first the ordered even numbers, followed by the ordered odd numbers). Notice how the returned string keys were used for comparison/sorting of the set of numbers.
Of course the lexicographical ordering here only works fine because numbers have the same length... But again: this is just a naive example of how to use the OrderBy() method.

keySelector is function delegate with parameter of type int that
returns TKey
Yes this is correct.
How to use this function to order items in objHash according custom
defined rules?
Firstly, note that OrderBy is an extension method on IEnumerable and not a contained method of HashSet.
if you want to order the elements via the OrderBy extension method then you'd pass x => x as the keySelector function:
var orderedEnumerable = objHash.OrderBy(x => x); // or any other logic as long as the function takes an int and returns TKey
Note that this doesn't modify the source and it wouldn't make sense to sort a HashSet anyway as by definition it does not care about order.
As explicitly stated in the documentation:
The HashSet<T> class provides high-performance set operations. A set
is a collection that contains no duplicate elements, and whose
elements are in no particular order.
it then goes on to say:
A HashSet<T> collection is not sorted and cannot contain duplicate
elements. If order or element duplication is more important than
performance for your application, consider using the List<T> class
together with the Sort method.
Emphasis mine.
if you want to maintain a sorted set then consider using SortedSet as it's documented as:
Represents a collection of objects that is maintained in sorted order.

Related

In C# how could I sort a jagged and 2d rectangular array based on 2nd column in ascending order, based on values not just alphabetical order?

I have a jagged or a simple 2d rectangular array (doesn't matter which one is sortable in anyway, I know how to convert between them back and forth). The array is in string format, since the first column is text, but the second column contains double values only. How could I sort the array in an ascending order based on their values not the alphabetical order?
The values would be for example these numbers: 10.0368; 27.2023; 15.21; 4.886
public static void Sort<T>(T[][] data, int col)
{
Comparer<T> comparer = Comparer<T>.Default;
Array.Sort<T[]>(data, (x, y) => comparer.Compare(x[col], y[col]));
Array.Reverse(data);
}
I used this extension method until now, but as it seems to me, it only sorts in alphabetical order. => 10.0368; 15.21; 27.2023; 4.886 (1; 1; 2; 4)
How I would like it to work would be obviously this: 4.886; 10.0368; 15.21; 27.2023.
I tried to find an answer on Google, and in some of the books I have about C#, but I only found the answer for arrays that would have only numbers in it.
If you need any more in depth details to solve the problem, I will provide you with the info you need.
I used this extension method until now, but as it seems to me, it only sorts in alphabetical order.
This is true, because you said earlier that your array is in string format. Hence, your
Comparer<T> comparer = Comparer<T>.Default;
gives you a string comparator, which compares based on lexicographic order.
If you know that the second element of the array contains a double, you can rewrite your code as follows:
public static void Sort<T>(T[][] data, int col) {
var comparer = Comparer<double>.Default;
Array.Sort<T[]>(
data
, (x, y) => comparer.Compare(
double.Parse(y[col].ToString())
, double.Parse(x[col].ToString())
)
);
}
x[col].ToString() does nothing because x[col] is already a string, but it lets you keep your code generic, instead of rewriting as non-generic string[][] method.
Note that you do not have to reverse the result if you reverse parameters that you pass to the comparator.

Two clone variables return the same index on a list

I have a list which has a number of objects from class X.
I add one object via clone function, it gets its own index, but if I add one more object using the clone, the object receives the same index as the first clone.
Here some code:
public void AddCopyObj(List<x> MyList, int Idx)
{
x AddCloneObj=MyList[Idx].Clone();
MyList.Insert(Idx+1,AddCloneObj)
}
public List<int> GetAllIndexs(List<x> MyList)
{
List<int> IndexList = new List<int>();
foreach(x myXvar in MyList)
{
IndexList.add(MyList.IndexOf(myXvar));
}
return IndexList ;
}
For example: If I have 10 objects to one of them I made twice clone, I will have 12 objects and index of both the clone be the same (they do not sit on the same index, the function IndexOf returns the same one)
What can I do?
EDIT:
public x Clone()
{
x clone = new x(Int32.Parse(this.Name.Split(new char[1] { ' ' })[1]) + 1);
clone.Copy(this);
return clone;
}
Quoted from MSDN (emphasis my own):
Searches for the specified object and returns the zero-based index of
the first occurrence within the range of elements in the List that
extends from the specified index to the last element.
They are both matching the first occurrence basically. This boils down to equality on the items you have in List<>, it uses the default equality comparer:
This method determines equality using the default equality comparer
EqualityComparer.Default for T, the type of values in the list.
http://msdn.microsoft.com/en-us/library/e4w08k17.aspx
You could use the override that takes a starting index to preclude prior indices from the search:
http://msdn.microsoft.com/en-us/library/s8t42k5w.aspx
Or, if you want to hold unique items based on hash and equality, use HashSet<T> instead.
I thought about offering a code sample, however when I look at the code you provide it makes less and less sense. You current sample will loop the items in index order and add the index to another list, only for duplicate items it'll be the same index. Taking a step back, what are you trying to achieve? I get the sense there's a better option.
The problem was I did not do twice clone,
I took the same object and put it twice in the list,
after I had done twice clone issue is resolved.
(Sorry, it was not on a question, you could not tell.)

How many times the CompareTo method is called when a collection is sorted?

If a type implements IComparable<T> and you have a collection of this type with 100 elements. When you call the Sort method on this collection, how many times would the CompareTo method be called and how? Would it be used in this manner?
CompareTo(item0, item1);
CompareTo(item1, item2);
CompareTo(item2, item3);
CompareTo(item3, item4);
...
CompareTo(item97, item98);
CompareTo(item98, item99);
EDIT: Basically what I am trying to do is to turn this way of sorting into a value-based sorting where I assign some value to each item and then sort them. It's hard to explain but I am not able to use a -1,0,1 based sorting function for this problem. But all I have is a CompareTo function that I need to use to sort the items. So I need to generate some values for each item, and then the program will sort them from the smallest value to largest.
Well, you can't be 100% sure (with most sorting algorithms) as it will depend on the data. For example, certain sorting algorithms will only perform N (N being the size of the collection) comparisons of the data is already sorted, but needs to be much more if it's not.
The commonly used sorting algorithms, such as MergeSort, QuickSort, and HeapSort are all O(n*log(n)), which is to say the number of comparisons will be on the order of the number of items times the log base of the number of items. (The log base will be 2 for those algorithms.) While this won't be exact, it will scale with that relationship.
If you're interested in how many times it's called for a particular sorting operation you can use something like this:
public class LoggingComparer<T> : IComparer<T>
{
private IComparer<T> otherComparer;
public LoggingComparer(IComparer<T> otherComparer)
{
this.otherComparer = otherComparer;
}
public int Count { get; private set; }
public int Compare(T x, T y)
{
Count++;
return otherComparer.Compare(x, y);
}
}
It will wrap another comparer but also count the number of compare calls. Here's an example usage:
var list = new List<int>() { 5, 4, 1, 2, 3, 8, 7, 9, 0 };
LoggingComparer<int> comparer = new LoggingComparer<int>(Comparer<int>.Default);
list.Sort(comparer);
Console.WriteLine(comparer.Count);
Piggy backing on Servy's answer. Whatever the Asymptotic Complexity for comparison operations of the sorting algorithm is, that is how many calls will likely be made to CompareTo(). Note, this is usually a growth pattern and not an exact number of operations.

c# - BinarySearch StringList with wildcard

I have a sorted StringList and wanted to replace
foreach (string line3 in CardBase.cardList)
if (line3.ToLower().IndexOf((cardName + Config.EditionShortToLong(edition)).ToLower()) >= 0)
{
return true;
}
with a binarySearch, since the cardList ist rather large(~18k) and this search takes up around 80% of the time.
So I found the List.BinarySearch-Methode, but my problem is that the lines in the cardList look like this:
Brindle_Boar_(Magic_2012).c1p247924.prod
But I have no way to generate the c1p... , which is a problem cause the List.BinarySearch only finds exact matches.
How do I modify List.BinarySearch so that it finds a match if only a part of the string matches?
e. g.
searching for Brindle_Boar_(Magic_2012) should return the position of Brindle_Boar_(Magic_2012).c1p247924.prod
List.BinarySearch will return the ones complement of the index of the next item larger than the request if an exact match is not found.
So, you can do it like this (assuming you'll never get an exact match):
var key = (cardName + Config.EditionShortToLong(edition)).ToLower();
var list = CardBase.cardList;
var index = ~list.BinarySearch(key);
return index != list.Count && list[index].StartsWith(key);
BinarySearch() has an overload that takes an IComparer<T> has second parameter, implement a custom comparer and return 0 when you have a match within the string - you can use the same IndexOf() method there.
Edit:
Does a binary search make sense in your scenario? How do you determine that a certain item is "less" or "greater" than another item? Right now you only provide what would constitute a match. Only if you can answer this question, binary search applies in the first place.
You can take a look at the C5 Generic Collection Library (you can install it via NuGet also).
Use the SortedArray(T) type for your collection. It provides a handful of methods that could prove useful. You can even query for ranges of items very efficiently.
var data = new SortedArray<string>();
// query for first string greater than "Brindle_Boar_(Magic_2012)" an check if it starts
// with "Brindle_Boar_(Magic_2012)"
var a = data.RangeFrom("Brindle_Boar_(Magic_2012)").FirstOrDefault();
return a.StartsWith("Brindle_Boar_(Magic_2012)");
// query for first 5 items that start with "Brindle_Boar"
var b = data.RangeFrom("string").Take(5).Where(s => s.StartsWith("Brindle_Boar"));
// query for all items that start with "Brindle_Boar" (provided only ascii chars)
var c = data.RangeFromTo("Brindle_Boar", "Brindle_Boar~").ToList()
// query for all items that start with "Brindle_Boar", iterates until first non-match
var d = data.RangeFrom("Brindle_Boar").TakeWhile(s => s.StartsWith("Brindle_Boar"));
The RageFrom... methods perform a binary search, find the first element greater than or equal to your argument, that returns an iterator from that position

IEnumerable<IEnumerable<int>> - no duplicate IEnumerable<int>s

I'm trying to find a solution to this problem:
Given a IEnumerable< IEnumerable< int>> I need a method/algorithm that returns the input, but in case of several IEnmerable< int> with the same elements only one per coincidence/group is returned.
ex.
IEnumerable<IEnumerable<int>> seqs = new[]
{
new[]{2,3,4}, // #0
new[]{1,2,4}, // #1 - equals #3
new[]{3,1,4}, // #2
new[]{4,1,2} // #3 - equals #1
};
"foreach seq in seqs" .. yields {#0,#1,#2} or {#0,#2,#3}
Sould I go with ..
.. some clever IEqualityComparer
.. some clever LINQ combination I havent figured out - groupby, sequenceequal ..?
.. some seq->HashSet stuff
.. what not. Anything will help
I'll be able to solve it by good'n'old programming but inspiration is always appreciated.
Here's a slightly simpler version of digEmAll's answer:
var result = seqs.Select(x => new HashSet<int>(x))
.Distinct(HashSet<int>.CreateSetComparer());
Given that you want to treat the elements as sets, you should have them that way to start with, IMO.
Of course this won't help if you want to maintain order within the sequences that are returned, you just don't mind which of the equal sets is returned... the above code will return an IEnumerable<HashSet<int>> which will no longer have any ordering within each sequence. (The order in which the sets are returned isn't guaranteed either, although it would be odd for them not to be return in first-seen-first-returned basis.)
It feels unlikely that this wouldn't be enough, but if you could give more details of what you really need to achieve, that would make it easier to help.
As noted in comments, this will also assume that there are no duplicates within each original source array... or at least, that they're irrelevant, so you're happy to treat { 1 } and { 1, 1, 1, 1 } as equal.
Use the correct collection type for the job. What you really want is ISet<IEnumerable<int>> with an equality comparer that will ignore the ordering of the IEnumerables.
EDITED:
You can get what you want by building your own IEqualityComparer<IEnumerable<int>> e.g.:
public class MyEqualityComparer : IEqualityComparer<IEnumerable<int>>
{
public bool Equals(IEnumerable<int> x, IEnumerable<int> y)
{
return x.OrderBy(el1 => el1).SequenceEqual(y.OrderBy(el2 => el2));
}
public int GetHashCode(IEnumerable<int> elements)
{
int hash = 0;
foreach (var el in elements)
{
hash = hash ^ el.GetHashCode();
}
return hash;
}
}
Usage:
var values = seqs.Distinct(new MyEqualityComparer()).ToList();
N.B.
this solution is slightly different from the one given by Jon Skeet.
His answer considers sublists as sets, so basically two lists like [1,2] and [1,1,1,2,2] are equal.
This solution don't, i.e. :
[1,2,1,1] is equal to [2,1,1,1] but not to [2,2,1,1], hence basically the two lists have to contain the same elements and in the same number of occurrences.

Categories