Sort array of items using OrderBy<> - c#

I have an array of items and I would like to sort on one of their properties.
I can access the items property using "item.Fields["FieldName"].Value" the property is returned as a string but I can cast it to an int.
I had a look at OrderBy<> but I have no idea of how to use it.

To be clear, OrderBy won't sort the array in place - it will return a new sequence which is a sorted copy of the array. If that's okay, then you want something like:
var sorted = array.OrderBy(item => item.Fields["FieldName"].Value);
On the other hand, I don't understand your comment that the property is returned as a string but that you can cast it to an int - you can't cast strings to ints, you have to parse them. If that's what you meant, you probably want:
var sorted = array.OrderBy(item => int.Parse(item.Fields["FieldName"].Value));
If you want that as an array, you can call ToArray() afterwards:
var sorted = array.OrderBy(item => int.Parse(item.Fields["FieldName"].Value))
.ToArray();
Alternatively you could use Array.Sort if you want to sort in-place, but that will be somewhat messier.

Use the Sort method to sort an array:
Array.Sort(theArray, (a, b) => String.Compare(a.Fields["FieldName"].Value, b.Fields["FieldName"].Value));
If you are not using C# 3, you use a delegate instead of a lambda expression:
Array.Sort(theArray, delegate(Item a, Item b) { return String.Compare(a.Fields["FieldName"].Value, b.Fields["FieldName"].Value); } );
(This also works with framework 2, which the OrderBy extension doesn't.)

If you can use orderby it should be easy, try the following. I threw in the int.Parse although depending on how you actually want to sort this might not be required.
var sorted = array.OrderBy(item => int.Parse(item.Fields["FieldName"].Value));

var sortedArray = items.OrderBy(i => i.property).ToArray();
If you don't want an array, you can leave that off in which case you will have an IEnumerable<> of type item.

It's worth mentioning that List<T>.Sort is based on quick sort, and in most cases it is not a stable sort.
This implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.
However you can use Enumberable.OrderBy which performs a stable sort.
This method performs a stable sort; that is, if the keys of two elements are equal, the order of the elements is preserved. In contrast, an unstable sort does not preserve the order of elements that have the same key.

Related

Sorting an Array C#

I am trying to sort an array before printing the coordinates to the console. I tried to resort the Ships array based on the Y coordinate. I need to reorder it so I can print them in order. i tried Array.Sort(myArray) - fails, I tried converting to a list or dictionary but not sure that is the best way. Any suggestions?
Need to resort the Ships array
Use the LINQ OrderBy() method. Assuming you want to sort by the YCoordinate of the last BoardPosition, and YCoordinate implements IComparable (e.g. is an int):
using System.Linq;
Ship[] ships;
Ship[] sorted = ships.OrderBy(s => s.BoardPositions.Last().YCoordinate).ToArray();
Edit The above code assumes that for every Ship in Ship[] ships, at least one entry in BoardPositions exists. If BoardPositions might be empty, use LastOrDefault() and the null-conditional operator ?..
Ship[] sorted = ships
.OrderBy(ship => ship.BoardPositions.Any())
.ThenBy(ship => ship.BoardPositions.LastOrDefault()?.YCoordinate)
.ToArray();
In order for the Array.Sort(myArray) to do what you expect, the elements of myArray (im going to assume ships) need to be comparable. That is you need to specify how you want them compared (presumably Y coordinate).
The approach to this is either have the ship class implement the IComparable interface or to use a different sort method whereby you would separately define a comparer to use.
I can't give example code as there isn't anything to go off in your question.

Sorting a list alphabetically with lambda after a certain position

Given a list with three elements that must stay at the top always:
Stay#Top1
Stay#Top2
Stay#Top3
Chicken
Bull
Zebra
Elephant
Hippo
Using lamdba expression, how can you sort this list alphabetically starting with "Chicken", and keeping the first three elements at the top?
Thanks ahead of time for any hints!
Take the three first items and then concatenate with the sorted remainder.
lst.Take(3).Concat(lst.Skip(3).OrderBy(s=>s);
Try using this:
list.Take(3).Concat(list.Skip (3).OrderBy (x => x.Name))
Given a List<T> where you wish to sort it but keep the first three elements at the start, you can use the overload of List<T>.Sort(int index, int count, IComparer<T> comparer) which lets you specify the range of elements to sort.
So you could do (assuming List<string>):
lst.Sort(3, lst.Length - 3, Comparer<string>.Default);
This doesn't use a lambda like you asked for - but I don't see why you need to use a lambda. ;)
An in-place sort is going to be much more efficient, if you can use it.
var final = lst.Take(3).ToList(); ;
var sortedSet = lst.Skip(3).OrderBy(x => x);
final.AddRange(sortedSet);

Efficiently pairing objects in lists based on key

So, here's the deal.
(My current use-case is in C#, but I'm also interested in the general algorithmic case)
I am given two Arrays of objects (I don't get to alter the code that creates these arrays, unfortunately).
Each object has (as part of it) a .Name property, a string.
These strings are unique per object, and they have zero or one matching strings in the other object.
What I need to do is efficiently pair these objects based on that string, into some sort of collection that allows me access to the paired objects. The strings need to match exactly to be considered a match, so I don't need any Upper or CaseInsensitive, etc.
Sadly, these lists are not sorted.
The lists themselves are maybe 30-50 items, but I need to repeat the algorithm on thousands of these array-pairs in a row, so efficiency is important.
Since I know that there's 0 or 1 match, and I know that most of them will be 1 match, I feel like there's a more efficient algorithm than x*y (Foreach item in x, foreach item in y, if x=y then x and y are a match)
I believe the most likely options are:
Keep the unsorted list and just do x*y, but drop items from the list once I've found them so I don't check ones already-found,
OR:
Convert both to Dictionaries and then do an indexed lookup on each (array2[currentArray1Item])
OR:
Sort the lists myself (Array.Sort()), and then having sorted arrays I can probably do something clever like jump to the index in B where I'd expect to find it (wherever it was in A) and then move up or down based on string until I either find it or pass where it should've been.
Then once that's done I need to figure out how to store it, I suppose I can make a custom ObjectPair class that just holds objects A and B. No need to do anything fancy here, since I'm just going to ForEach on the pairs.
So the questions are:
Are any of the above algorithms the fastest way to do this (if not, what is?) and is there some existing C# structure that'd conveniently hold the found pairs?
EDIT: Array.Sort() is a method that exists, so I don't need to convert the array to List to sort. Good to know. Updated above.
The question I have is: how much efficiency do we gain from the special handling if it requires us to sort both input arrays? According to the documentation for Array.Sort, it is O(n log n) on average and O(n ^ 2) in the worst case (quicksort). Once we have both arrays sorted, we then have another O(n) amount of work because we have to loop through the first one.
I interpret this to mean that the overall amount of work might actually increase because of the number of iterations required to sort, then process. This of course would be a different story if you could guarantee sorted arrays at the start, but as you said you cannot. (I should also note that you would need to create a custom IComparer<T> implementation to pass to Array.Sort so it knows to use the .Name property. That's not runtime work, but it's still work :-)
You might consider using a LINQ join, which only iterates the inner array a single time (see here for psuedocode). This is as opposed to the nested foreach statements, which would iterate the inner array for each element of the outer array. It's about as efficient as it can be in the general case and doesn't introduce the complexity of the special handling you suggested.
Here is an example implementation:
var pairs =
from item1 in array1
join item2 in array2 on item1.Name equals item2.Name
select new { item1, item2 };
foreach(var pair in pairs)
{
// Use the pair somehow
}
That very clearly states what you are doing with the data and also gives you an anonymous type representing each pair (so you don't have to invent a pairing). If you do end up going a different route, I would be interested in how it compares to this approach.
Sort the second array using Array.Sort method, then match objects in the second Array using Binary Search Algorithm.
Generally, for 30-50 items this would be a little faster than brute force x*y.

how to compare two lists using C#?

I have two lists how can I check if list1 has some items that are from list2
for ie i have:
list1 = ["car","424", "fwe"]
list2 = ["car", "cat"]
maybe something like this:
if list1 has elements from list2
then return true
You can use LINQ Intersect, Except functions.
You can use Intersect with Any:
list1.Intersect(list2).Any()
The best solution really depends on the specifics of your situation.
For instance, you could compare each pair of elements, which would be a very straightforward implementation. However, this isn't particularly efficient if the lists are long.
A second option would be to add all the elements of one list to a HashSet, and then try to add all the elements of the second list. If there is an element in common, the HashSet Add() method will return false when you try to add the duplicate. This will be faster for large lists, but requires additional memory, and may produce less readable code.
Another possible solution:
list1.Any(e => list2.Contains(e));

Comparing 2 lists using Linq

I have 2 lists I am trying to compare. I execute the following and I get a false value returned:
var areIdentical = list1.SequenceEqual(list2, myFileCompare);
That part is working. My lists are NOT equal. The problem is, I'm using the following command to try to find the differences:
var fileDiff = (from file in list1
select file).Except(list2, myFileCompare);
My problem is, fileDiff is returning an empty result set. Since I know they are NOT identical, shouldn't I get something returned? Perhaps my query is wrong on this. Any help would be appreciated! By the way, I can post more of my code, if you really need it, however, this should suffice.
You wouldn't get anything if:
list2 contained everything in list1 but also extra items
the ordering was different
Assuming you don't care about the ordering, you can use:
var extraItemsInList2 = list2.Except(list1);
var extraItemsInList1 = list1.Except(list2);
If you do care about the order, you'll need to work out exactly how you want the differences to be represented.
SequenceEqual() will return true only if the elements as well as the element sequence is the same.
Except() will compare only the elements, not the sequence.
Your two lists obviously have different sequences but, judging by the behavior you've posted, I'm guessing they both contain the same elements.
If you are after the symmetric difference (all differences between either list, ordering is not important) then you could use the following which will be heavy on the computation but should do the trick:
var fileDiff = list1.Union(list2).Except(list1.Intersect(list2));
Or (as per Jon Skeet's answer):
var fileDiff = list1.Except(list2).Union(list2.Except(list1));
I'll leave it to the rest of the community to show you a more efficient way to do it... But this is the most obvious "linq" way that I can think of...
SequenceEqual cares about sequence (which is kind of hinted in its name ;) ), but Except doesn't.
So it is entirely possible that list2 contains same elements as list1, but in different order, so SequenceEqual returns false yet Except returns no elements.
It is also possible that list2 is a proper super-set of the list1, in which case SequenceEqual returns false regardless of order, and Except still returns no elements.
If you want to work with set operations, you'll probably be better off using some set-like container such as HashSet or SortedSet directly. In your case, you might be interested in HashSet.SetEquals and/or HashSet.ExceptWith.

Categories

Resources