Some misunderstanding about sort extensions - c#

I have the following code example:
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
list.Add(6);
list.Add(7);
list.OrderByDescending(n=>n).Reverse();
But when I use this:
list.OrderByDescending(n=>n).Reverse();
I don't get wanted result.
If instead of the above statement, I use this one:
list.Reverse();
I get the wanted result.
Any idea why I don't get wanted result using the first statement ?
I believe I am missing something in understanding the extensions.
Thank you in advance.

The list.Reverse() method reverses the list in-place, so your original list is changed.
The .OrderByDescending() extension method produces a new list (or rather an IEnumerable<T>) and leaves your original list intact.
EDIT
To get two lists, for both sort orders:
List<int> upList = list.OrderBy(n => n).ToList();
List<int> downList = list.OrderByDescending(n => n).ToList();

Edit: So the problem seems to be that you think that the Enumerable extensions would change the original collection. No they do not. Actually they return something new you need to asign to a variable:
IEnumerable<int> ordered = list.OrderByDescending(n => n);
foreach(int i in ordered)
Console.WriteLine(i);
OrderByDescending orders descending(highest first) which is what you obviously want. So i don't understand why you reverse it afterwards.
So this should give you the expected result:
var ordered = list.OrderByDescending(n=> n);
This returns an "arbitrary" order:
list.Reverse()
since it just reverses the order you have added the ints. If you have added them in an ordered way you don't need to order at all.
In general: use OrderBy or OrderByDescending if you want to order a sequence and Reverse if you want to invert the sequence what is not necessarily an order (it is at least confusing).

Related

Sort based on function

I have a method that given 2 strings he returns a number (between 0 and 100) which represents is how alike they are, being 0 "not similar at all" and 100 "they are the same"
Now the thing is that i have a list of County (string name, GeoRef coordinates, string Mayor) which i would like to sort based on the return of my function...
im looking for something like myList.Sort(f=>MyScoreEvaluator("York",f.Name))
Can anyone tell me how to do so?
Edit1: I dont think that the method "Sort" is quite i want... Sort compare itens inside of the list... i want to compare the itens of the list against a external info and based on that result sort the items
The OrderBy and OrderByDescending are returning the same item order...
Edit2: Heres is the code of the OrderBy I'm using: aux.OrderBy(f => StringComparisonHelper.HowAlike(f.Name, countyNameSearched));
You can use OrderBy, and re-assign your list:
list = list.OrderBy(f => MyScoreEvaluator("York", f.Name))
You could just use OrderBy:
list.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or Implement a custom Comparer:
public static int SortByName(County x, County y)
{
return x.Name.CompareTo(y.Name);
}
Usage:
list.Sort(new Comparison<County>(SortByName))
There is an OrderBy in LINQ:
var sorted = myList.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or to sort descendingly:
var sortedDesc = myList.OrderByDescending(f => MyScoreEvaluator("York", f.Name))
It's very easy to use the LINQ OrderBy extension (see others' answers).
If you want to use Sort, it would be:
myList.Sort((x, y) => MyScoreEvaluator("York", x.Name)
.CompareTo(MyScoreEvaluator("York", y.Name)));
This assumes that myList is a System.Collections.Generic.List<>.
If you want the other sort direction, swap x and y on one side of the lambda arrow =>, of course.
EDIT:
Remember .Sort method on List<> modifies the same instance. The return type of Sort method is void. On the other hand, OrderBy creates a new IEnumerable<> on which you can call .ToList() to get a new list object. The old object is unchanged. You might assign the new object to the variable that held the original list. Other variables that reference the old object won't be affected by that. Example:
myList = myList.OrderBy(f => MyScoreEvaluator("York", f.Name)).ToList();
NEW EDIT:
If performance is an issue, it's not clear which of these two to use. The OrderBy method calls the MyScoreEvaluator only once per item in your original list. The Sort method as presented here, calls MyScoreEvaluator a lot more times, because it doesn't "remember" the result of each MyScoreEvaluator call (the Comparison<> delegate instance is a black box to the Sort algorithm). So if it wants to compare "Fork" and "Kork", it calls MyScoreEvaluator twice. Then afterwards if it wants to compare "Kork" and "Yorc", it does the "Kork" MyScoreEvaluator again. On the other hand, the sort algorithm of List<>.Sort is superior to that of OrderBy.

Remove sublist from a list

I have 2 lists: list1 and list2 (both of type int)
Now I want to remove content of list2 from list1. How I can do this in C#?
PS: Don't use loop.
IMPORTANT CHANGE
As was pointed out in the comments, .Except() uses a set internally, so any duplicate members of list1 will be absent in the final result.
Produces the set difference of two sequences
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except(v=vs.110).aspx
However, there is a solution that is both O(N) and preserves duplicates in the original list: Modify the RemoveAll(i => list2.Contains(i)) approach to use a HashSet<int> to hold the exclusion set.
List<int> list1 = Enumerable.Range(1, 10000000).ToList();
HashSet<int> exclusionSet = Enumerable.Range(500000, 10).ToHashSet();
list1.Remove(i => exclusionSet.Contains(i));
The extension method ToHashSet() is available in MoreLinq.
ORIGINAL ANSWER
You can use Linq
list1 = list1.Except(list2).ToList();
UPDATE
Out of curiosity I did a simple benchmark of my solution vs. #HighCore's.
For list2 having just one element, his code is faster. As list2 gets larger and larger, his code gets extremely slow. It looks like his is O(N-squared) (or more specifically O(list1.length*list2.length) since each item in list1 is compared to each item in list2). Don't have enough data points to check the Big-O of my solution, but it is much faster when list2 has more than a handful of elements.
Code used to test:
List<int> list1 = Enumerable.Range(1, 10000000).ToList();
List<int> list2 = Enumerable.Range(500000, 10).ToList(); // Gets MUCH slower as 10 increases to 100 or 1000
Stopwatch sw = Stopwatch.StartNew();
//list1 = list1.Except(list2).ToList();
list1.RemoveAll(i => list2.Contains(i));
sw.Stop();
var ms1 = sw.ElapsedMilliseconds;
UPDATE 2
This solution assigns a new list to the variable list1. As #Толя points out, other references (if any) to the original list1 will not be updated. This solution drastically outperforms RemoveAll for all but the smallest sizes of list2. If no other references must see the update, it is preferable for that reason.
list1.RemoveAll(x => list2.Contains(x));
You can use this:
List<T> result = list1.Except(list2).ToList();
This will remove every item in the secondList from the firstList:
firstList.RemoveAll( item => { secondList.Contains(item); } );

Sort an int array with orderby

I would like to sort my int array in ascending order.
first I make a copy of my array:
int[] copyArray = myArray.ToArray();
Then I would like to sort it in ascending order like this:
int[] sortedCopy = from element in copyArray
orderby element ascending select element;
But I get a error, "selected" gets highligted and the error is:
"cannot implicitly convert type 'system.linq.iorderedenumerable' to 'int[]'"
You need to call ToArray() at the end to actually convert the ordered sequence into an array. LINQ uses lazy evaluation, which means that until you call ToArray(), ToList() or some other similar method the intermediate processing (in this case sorting) will not be performed.
Doing this will already make a copy of the elements, so you don't actually need to create your own copy first.
Example:
int[] sortedCopy = (from element in myArray orderby element ascending select element)
.ToArray();
It would perhaps be preferable to write this in expression syntax:
int[] sortedCopy = myArray.OrderBy(i => i).ToArray();
Note: if you don't need a copy (i.e. it is acceptable to change myArray), then a much simpler and more efficient approach is just:
Array.Sort(myArray);
This does an in-place sort of the array, exploiting the fact that it is an array to be as efficient as possible.
For more complex scenarios (for example, a member-wise sort of an object-array), you can do things like:
Array.Sort(entityArray, (x,y) => string.Compare(x.Name, y.Name));
this is the moral-equivalent of:
var sortedCopy = entityArray.OrderBy(x => x.Name).ToArray();
but again: doing the sort in-place.
We don't know what you are doing next, but maybe you don't need an array. If it's going into another linq statement, or foreach, then just keep it as it is, most simply by using var.
var sortedCopy = myArray.OrderBy(i => i);
foreach(var item in sortedCopy)
{
//print out for example
}
This allows linq to be as lazy as possible. If you always cast ToArray or ToList then it has no choice than to evaluate then and there, and allocate memory for the result.

How to delete multiple entries from a List without going out of range?

I have a list of integers that contains a number of values (say, 200).
List<int> ExampleList;
And another list on integers that holds the indexes that need to be deleted from ExampleList. However, this list is not sorted.
List<int> RemoveFromExampleList;
If it were sorted, I would have run a reverse loop and deleted all the values like this:
for (int i = (RemoveFromExampleList.Count-1); i >=0; i--)
{
ExampleList.RemoveAt(RemoveFromExampleList[i]);
}
Do I have to sort RemoveFromExampleList, or is there another way to prune the unnecessary values from ExampleList?
If I do have to sort, whats the easiest way to sort? Is there any inbuilt C# library/method to sort?
If RemoveFromExampleList is a list of indexes, you would have to sort it and work in descending order to delete based on those indexes. Doing it any other way would cause you to delete values you don't mean to delete.
Here is the one liner.
ExampleList.RemoveAll(x => RemoveFromExampleList.Contains(ExampleList.IndexOf(x)));
You could replace the values you are going to remove with a sentinel value, i.e., one that you know doesn't occur in the list, and then remove all occurrences of that value.
Your option is to sort, yes. Sort the removal list in descending order and then remove by index that way.
// perform an orderby projection, remove
foreach (int index in RemoveFromExampleList.OrderByDescending(i => i)
ExampleList.RemoveAt(index);
Or
// actually sort the list, then remove
RemoveFromExampleList.Sort((a,b) => b.CompareTo(a));
foreach (int index in RemoveFromExampleList)
ExampleList.RemoveAt(index);
(Assumes there are no duplicates, use .Distinct() on the list/projection if otherwise.)
If you really had some aversion to sorting the list, you could make the list a list of nullable ints:
List<int?> ints;
Then you could nullify the values in the "delete list", and use the RemoveAll method to delete the null values.
But this is obviously a bit of a hack.
You could do it with LINQ / Lambda like so:
//EXAMPLE TO REMOVE ITEMS COMING FROM ANOTHER LIST
List masterList = new List();
masterList.Add(1);
masterList.Add(1);
masterList.Add(2);
masterList.Add(3);
List<int> itemsToRemove = new List<int>();
itemsToRemove.Add(1);
itemsToRemove.Add(2);
itemsToRemove.Add(3);
List<int> cleanList = new List<int>();
foreach (int value in itemsToRemove)
{
masterList = masterList.Where(x => x != value).ToList();
}

How can I sort generic list DESC and ASC?

How can I sort generic list DESC and ASC? With LINQ and without LINQ? I'm using VS2008.
class Program
{
static void Main(string[] args)
{
List<int> li = new List<int>();
li.Add(456);
li.Add(123);
li.Add(12345667);
li.Add(0);
li.Add(1);
li.Sort();
foreach (int item in li)
{
Console.WriteLine(item.ToString() + "\n");
}
Console.ReadKey();
}
}
With Linq
var ascendingOrder = li.OrderBy(i => i);
var descendingOrder = li.OrderByDescending(i => i);
Without Linq
li.Sort((a, b) => a.CompareTo(b)); // ascending sort
li.Sort((a, b) => b.CompareTo(a)); // descending sort
Note that without Linq, the list itself is being sorted. With Linq, you're getting an ordered enumerable of the list but the list itself hasn't changed. If you want to mutate the list, you would change the Linq methods to something like
li = li.OrderBy(i => i).ToList();
Without Linq:
Ascending:
li.Sort();
Descending:
li.Sort();
li.Reverse();
without linq,
use Sort() and then Reverse() it.
I was checking all the answer above and wanted to add one more additional information.
I wanted to sort the list in DESC order and I was searching for the solution which is faster for bigger inputs and I was using this method earlier :-
li.Sort();
li.Reverse();
but my test cases were failing for exceeding time limits, so below solution worked for me:-
li.Sort((a, b) => b.CompareTo(a));
So Ultimately the conclusion is that 2nd way of Sorting list in Descending order is bit faster than the previous one.
Very simple way to sort List with int values in Descending order:
li.Sort((a,b)=> b-a);
Hope that this helps!

Categories

Resources