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.
Related
Having issues with the OrderBy clause not having any impact on the sort. I have walked through this in the debugger and insuring this is a case that the sort line of the code is being hit and reviewing the results after it the order by has not been applied.
public static IEnumerable<DDLOptions<TValueType>> GetDDLOptionsViewModel<TClass, TValueType>(
IEnumerable<TClass> list,
Func<TClass, TValueType> value,
Func<TClass, string> displayText,
bool sort = true
)
{
List<DDLOptions<TValueType>> ddlOptions;
ddlOptions = list.Select(
l => new DDLOptions<TValueType>
{
Value = value(l),
DisplayText = displayText(l)
}
).ToList(); <========== Works if I put the Order By here.
if (sort)
{
ddlOptions.OrderBy(l => l.DisplayText); <===== Does NOT work here.
}
return ddlOptions;
}
OrderBy returns a query that would perform the ordering: it does not modify the original list (whereas something like List<T>.Sort would modify the original)
Instead try something like:
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText).ToList();
You might want to play around with the type of ddlOptions or where/how you return the data as we're doing an extra ToList than probably necessary, but that's probably a minor/non-issue for this case anyway.
Note that the same applies to other LINQ functions like GroupBy, Distinct, Concat - all return results rather than modifying the source collection.
Try:
if (sort)
{
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText); <===== Should work now.
}
As others have said, you need to assign the result of OrderBy to something as it doesn't mutate the sequence it acts on. It's easiest to make ddlOptions an IEnumerable instead of a List, so that you can assign the result to that. The ToList call on the select is also not needed:
public static IEnumerable<DDLOptions<TValueType>> GetDDLOptionsViewModel<TClass, TValueType>(
IEnumerable<TClass> list,
Func<TClass, TValueType> value,
Func<TClass, string> displayText,
bool sort = true
)
{
IEnumerable<DDLOptions<TValueType>> ddlOptions;
ddlOptions = list.Select(
l => new DDLOptions<TValueType>
{
Value = value(l),
DisplayText = displayText(l)
}
);
if (sort)
{
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText);
}
return ddlOptions;
}
Note that this version of the method will use deferred execution, and so won't actually perform the Select/OrderBy until the sequence is iterated. If you don't want to do that, you can add ToList on the return line.
You need to type:
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText);
OrderBy doesn't sort a List<T> or any other IEnumerable<T>. It produces a new, sorted IEnumerable<T>. So calling ddlOptions.OrderBy(...) doesn't modify ddlOptions.
If you have a List<T> and wish to sort it, you can use the Sort method - in particular the overload that takes a Comparison<T> as a parameter. This actually sorts the list instead of returning a new IEnumerable.
Comparison<T> is a delegate representing a function that takes two of T and returns a negative number if the first is "less" than the second, a positive number if the first is "greater" than the second, and zero if one isn't sorted before or after the other.
In this case you don't have to remember that. Instead, you can just do this:
ddlOptions.Sort((x, y) => string.CompareOrdinal(x.DisplayText, y.DisplayText));
You're passing in a function that takes two items in the list and returns the comparison result of their DisplayText properties, which will be negative, 0, or positive.
Sometimes we use OrderBy because it doesn't modify the original list. But if modifying the list is what we want then we can use Sort.
I have a HashSet of ID numbers, stored as integers:
HashSet<int> IDList; // Assume that this is created with a new statement in the constructor.
I have a SortedList of objects, indexed by the integers found in the HashSet:
SortedList<int,myClass> masterListOfMyClass;
I want to use the HashSet to create a List as a subset of the masterListOfMyclass.
After wasting all day trying to figure out the Linq query, I eventually gave up and wrote the following, which works:
public List<myclass> SubSet {
get {
List<myClass> xList = new List<myClass>();
foreach (int x in IDList) {
if (masterListOfMyClass.ContainsKey(x)) {
xList.Add(masterListOfMyClass[x]);
}
}
return xList;
}
private set { }
}
So, I have two questions here:
What is the appropriate Linq query? I'm finding Linq extremely frustrating to try to figuere out. Just when I think I've got it, it turns around and "goes on strike".
Is a Linq query any better -- or worse -- than what I have written here?
var xList = IDList
.Where(masterListOfMyClass.ContainsKey)
.Select(x => masterListOfMyClass[x])
.ToList();
If your lists both have equally large numbers of items, you may wish to consider inverting the query (i.e. iterate through masterListOfMyClass and query IDList) since a HashSet is faster for random queries.
Edit:
It's less neat, but you could save a lookup into masterListOfMyClass with the following query, which would be a bit faster:
var xList = IDList
.Select(x => { myClass y; masterListOfMyClass.TryGetValue(x, out y); return y; })
.Where(x => x != null)
.ToList();
foreach (int x in IDList.Where(x => masterListOfMyClass.ContainsKey(x)))
{
xList.Add(masterListOfMyClass[x]);
}
This is the appropriate linq query for your loop.
Here the linq query will not effective in my point of view..
Here is the Linq expression:
List<myClass> xList = masterListOfMyClass
.Where(x => IDList.Contains(x.Key))
.Select(x => x.Value).ToList();
There is no big difference in the performance in such a small example, Linq is slower in general, it actually uses iterations under the hood too. The thing you get with ling is, imho, clearer code and the execution is defered until it is needed. Not i my example though, when I call .ToList().
Another option would be (which is intentionally the same as Sankarann's first answer)
return (
from x in IDList
where masterListOfMyClass.ContainsKey(x)
select masterListOfMyClass[x]
).ToList();
However, are you sure you want a List to be returned? Usually, when working with IEnumerable<> you should chain your calls using IEnumerable<> until the point where you actually need the data. There you can decide to e.g. loop once (use the iterator) or actually pull the data in some sort of cache using the ToList(), ToArray() etc. methods.
Also, exposing a List<> to the public implies that modifying this list has an impact on the calling class. I would leave it to the user of the property to decide to make a local copy or continue using the IEnumerable<>.
Second, as your private setter is empty, setting the 'SubSet' has no impact on the functionality. This again is confusing and I would avoid it.
An alternate (an maybe less confusing) declaration of your property might look like this
public IEnumerable<myclass> SubSet {
get {
return from x in IDList
where masterListOfMyClass.ContainsKey(x)
select masterListOfMyClass[x]
}
}
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.
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).
I have a list of DrawObject[]. Each DrawObject has a Rectangle property. Here is my event:
List<Canvas.DrawObject[]> matrix;
void Control_MouseMove ( object sender, MouseEventArgs e )
{
IEnumerable<Canvas.DrawObject> tile = Enumerable.Range( 0, matrix.Capacity - 1)
.Where(row => Enumerable.Range(0, matrix[row].Length -1)
.Where(column => this[column, row].Rectangle.Contains(e.Location)))
.????;
}
I am not sure exactly what my final select command should be in place of the "????". Also, I was getting an error: cannot convert IEnumerable<int> to bool.
I've read several questions about performing a linq query on a list of arrays, but I can't quite get what is going wrong with this. Any help?
Edit:
Apologies for not being clear in my intentions with the implementation.
I intend to select the DrawObject that currently contains the mouse location.
It's not at all clear what you're trying to do. I suspect you want something like:
var drawObjects = from array in matrix
from item in array
where item.Rectangle.Contains(e.Location)
select item;
... but maybe not. You haven't shown what you're trying to do with the result of the query, or what this[column, row] is there for.
You almost certainly don't want to be using the capacity of the list in the first place - it's more likely that you're interested in the Count, but using the list as an IEnumerable<T> is probably better anyway.
EDIT: Okay, so the above query finds all the drawObjects where the rectangle contains the given location. You almost certainly want to use something like First, FirstOrDefault, Single or SingleOrDefault. For example:
var drawObject = (from array in matrix
from item in array
where item.Rectangle.Contains(e.Location)
select item)
.SingleOrDefault();
if (drawObject != null) // We found one
{
...
}
var tile = matrix.SelectMany(x => x)
.Where(x => x.Rectangle.Contains(e.Location));
Maybe:
....Select(y => y);
But it is hard to really tell what you are doing. And your first Where clause will not work since the lambda expression in the clause must result in a bool, but your lambda expression is resulting in a IEnumerable<T>. If I'm not all wrong.