Sorting an Array C# - 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.

Related

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.

In C#, is there a kind of a SortedList<double> that allows fast querying (with LINQ) for the nearest value?

I am looking for a structure that holds a sorted set of double values. I want to query this set to find the closest value to a specified reference value.
I have looked at the SortedList<double, double>, and it does quite well for me. However, since I do not need explicit key/value pairs. this seems to be overkill to me, and i wonder if i could do faster.
Conditions:
The structure is initialised only once, and does never change (no insert/deletes)
The amount of values is in the range of 100k.
The structure is queried often with new references, which must execute fast.
For simplicity and speed, the set's value just below of the reference may be returned, not actually the nearest value
I want to use LINQ for the query, if possible, for simplicity of code.
I want to use no 3rd party code if possible. .NET 3.5 is available.
Speed is more importand than memory footprint
I currently use the following code, where SortedValues is the aforementioned SortedList
IEnumerable<double> nearest = from item in SortedValues.Keys
where item <= suggestion
select item;
return nearest.ElementAt(nearest.Count() - 1);
Can I do faster?
Also I am not 100% percent sure, if this code is really safe. IEnumerable, the return type of my query is not by definition sorted anymore. However, a Unit test with a large test data base has shown that it is in practice, so this works for me. Have you hints regarding this aspect?
P.S. I know that there are many similar questions, but none actually answers my specific needs. Especially there is this one C# Data Structure Like Dictionary But Without A Value, but the questioner does just want to check the existence not find anything.
The way you are doing it is incredibly slow as it must search from the beginning of the list each time giving O(n) performance.
A better way is to put the elements into a List and then sort the list. You say you don't need to change the contents once initialized, so sorting once is enough.
Then you can use List<T>.BinarySearch to find elements or to find the insertion point of an element if it doesn't already exist in the list.
From the docs:
Return Value
The zero-based index of
item in the sorted List<T>,
if item is found; otherwise, a
negative number that is the bitwise
complement of the index of the next
element that is larger than item or,
if there is no larger element, the
bitwise complement of Count.
Once you have the insertion point, you need to check the elements on either side to see which is closest.
Might not be useful to you right now, but .Net 4 has a SortedSet class in the BCL.
I think it can be more elegant as follows:
In case your items are not sorted:
double nearest = values.OrderBy(x => x.Key).Last(x => x.Key <= requestedValue);
In case your items are sorted, you may omit the OrderBy call...

Customizing Sort Order of C# Arrays

This has been bugging me for some time now. I've tried several approaches and none have worked properly.
I'm writing and IRC client and am trying to sort out the list of usernames (which needs to be sorted by a users' access level in the current channel).
This is easy enough. Trouble is, this list needs to added to whenever a user joins or leaves the channel so their username must be removed the list when the leave and re-added in the correct position when they rejoin.
Each users' access level is signified by a single character at the start of each username. These characters are reserved, so there's no potential problem of a name starting with one of the symbols. The symbols from highest to lowest (in the order I need to sort them) are:
~
&
#
%
+
Users without any sort of access have no symbol before their username. They should be at the bottom of the list.
For example: the unsorted array could contain the following:
~user1 ~user84 #user3 &user8 +user39 user002 user2838 %user29
And needs to be sorted so the elements are in the following order:
~user1 ~user84 &user8 #user3 %user29 +user39 user002 user2838
After users are sorted by access level, they also need to be sorted alphabetically.
Asking here is a last resort, if someone could help me out, I'd very much appreciate it.
Thankyou in advance.
As long as the array contains an object then implement IComparable on the object and then call Array.Sort().
Tho if the collection is changable I would recommend using a List<>.
You can use SortedList<K,V> with the K (key) implementing IComparable interface which then defines the criteria of your sort. The V can simply be null or the same K object.
You can give an IComparer<T> or a Comparison<T> to Array.Sort. Then you just need to implement the comparison yourself. If it's a relatively complex comparison (which it sounds like this is) I'd implement IComparer<T> in a separate class, which you can easily unit test. Then call:
Array.Sort(userNames, new UserNameComparer());
You might want to have a convenient instance defined, if UserNameComparer has no state:
Array.Sort(userNames, UserNameComparer.Instance);
List<T> has similar options for sorting - I'd personally use a list rather than an array, if you're going to be adding/removing items regularly.
In fact, it sounds like you don't often need to actually do a full sort. Removing a user doesn't change the sort order, and inserting only means inserting at the right point. In other words, you need:
Create list and sort it to start with
Removing a user is just a straightforward operation
Adding a user requires finding out where to insert them
You can do the last step using Array.BinarySearch or List.BinarySearch, which again allow you to specify a custom IComparer<T>. Once you know where to insert the user, you can do that relatively cheaply (compared with sorting the whole collection again).
You should take a look at the IComparer interface (or it's generic version). When implementing the CompareTo method, check whether either of the two usernames contains one of your reserved character. If neither has a special reserved character or both have the same character, call the String.CompareTo method, which will handle the alphabetical sorting. Otherwise use your custom sorting logic.
I gave the sorting a shot and came up with the following sorting approach:
List<char> levelChars = new List<char>();
levelChars.AddRange("+%#&~".ToCharArray());
List<string> names = new List<string>();
names.AddRange(new[]{"~user1", "~user84", "#user3", "&user8", "+user39", "user002", "user2838", "%user29"});
names.Sort((x,y) =>
{
int xLevel = levelChars.IndexOf(x[0]);
int yLevel = levelChars.IndexOf(y[0]);
if (xLevel != yLevel)
{
// if xLevel is higher; x should come before y
return xLevel > yLevel ? -1 : 1;
}
// x and y have the same level; regular string comparison
// will do the job
return x.CompareTo(y);
});
This comparison code can just as well live inside the Compare method of an IComparer<T> implementation.

Sort array of items using OrderBy<>

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.

Categories

Resources