c# - Sorting Multiple String arrays with Quicksort - c#

Sorry for the vague title but I'll try and describe what my problem as best as I can below.
Basically I have 5 string arrays that all hold data relevant to the same index in the other arrays. For example, element 5 in array 1 corresponds to element 5 in arrays 2, 3, 4 and 5.
What I have done is used the Quicksort algorthim to sort array 1 into alphabetical order. The problem is that when the array is sorted, no longer do elements in the other arrays correspond since the other arrays haven't been sorted.
What I need is some way to swap the same elements around in the other 4 arrays as has been down to array 1. For example, if element 2 in array 1 is swapped to element 55, then element 2 in the other 4 arrays need to be swapped to element 55 in their array and vice versa.
The end goal is to display all the data in a specific element across all 5 arrays.
Below I have added the quicksort algorithm I'm using and added 3 example arrays that need sorting:
string[] array1 = {"z","y","x","a"};
string[] array2 = {"26","25","24","1"};
string[] array3 = { "black","yellow","white","red" };
// The first 2 arrays should clarify my point further.
// I use Quicksort to sort array 1
public static void QuicksortSTRING(IComparable[] elements, int left, int right)
{        
int i = left, j = right;
IComparable pivot = elements[(left + right) / 2];
while (i <= j)
 {
while (elements[i].CompareTo(pivot) < 0)
{
i++;
}
while (elements[j].CompareTo(pivot) > 0)
{
j--;
}
if (i <= j)
{
// Swap
IComparable tmp = elements[i];
elements[i] = elements[j];
elements[j] = tmp;
i++;
j--;
}
}
// Recursive calls
if (left < j)
{
QuicksortSTRING(elements, left, j);
}
if (i < right)
{
QuicksortSTRING(elements, i, right);
}
}
If you need any other info just ask.

It’s better to put the three related strings into a single object:
sealed class RelatedInformation // or struct, you decide
{
public string First;
public string Second;
public string Third;
}
and then sort a list of those objects:
var myList = new List<RelatedInformation>();
// insert code that populates the list here
myList.Sort((a, b) => a.First.CompareTo(b.First));
or, if it needs to be an array:
var myArray = /* obtain the RelatedInformation[] here */;
Array.Sort(myList, (a, b) => a.First.CompareTo(b.First));
Additionally, there is no need for you to implement Quicksort yourself (unless this is homework? :)). You can just use Array.Sort or List<T>.Sort with a lambda expression that specifies your sort criterion.
You don’t even need to implement the IComparable<T> interface if you use the above code. However, if the RelatedInformation class (or struct) is used in many places that have something to do with their ordering, it may be wise to implement it anyway; then you can ditch the lambdas:
sealed class RelatedInformation : IComparable<RelatedInformation>
{
public string First;
public string Second;
public string Third;
public int CompareTo(RelatedInformation other)
{
return First.CompareTo(other.First);
}
}
// ...
var myList = new List<RelatedInformation>();
// insert code that populates the list
myList.Sort();
However, since you explicitly asked about the three-array situation, here is a solution that will work under that constraint. Instead of sorting any one of the arrays, the idea is to sort a list of the indexes. I’m going to use LINQ for this because it’s pretty succint and readable:
var sortedIndexes = Enumerable.Range(0, array1.Length)
.OrderBy(i => array1[i])
.ToArray();
var sortedArray1 = sortedIndexes.Select(i => array1[i]).ToArray();
var sortedArray2 = sortedIndexes.Select(i => array2[i]).ToArray();
var sortedArray3 = sortedIndexes.Select(i => array3[i]).ToArray();
Pretty short, huh? Of course, in the call to OrderBy, you can specify any other array to sort by.
Do be aware though that this code will throw an exception if any of the arrays is shorter than the first one, and it will silently discard items if any of the arrays is longer than the first one. One major benefit of the list-of-objects solution is that you do not need to worry about that.
As an added piece of information, the OrderBy from LINQ is a stable sort; this means that items where array1 has the same string stay in the same order. Array.Sort and List<T>.Sort do not have a stable sort.
You can even use this method to sort by multiple criteria; for example, let’s say you want to sort by the strings in array1, but whenever array1 has the same string for some items, you want those items to be sorted by whatever is in array2. You can do that using ThenBy:
var sortedIndexes = Enumerable.Range(0, array1.Length)
.OrderBy(i => array1[i])
.ThenBy(i => array2[i])
.ToArray();

You've got these three items of information to sort. Try creating a class to hold them. It can be an inner class inside one of your program classes if you want.
struct MyThing :IComparable {
char a;
int b;
string c;
}
Then make a List<MyThing>. Then populate it with your data.
You'll need to implement the IComparable interface (requiring your own CompareTo method) for your class, so it knows to sort on a, or whatever you want sorted.
Then use the built in List.Sort() function or your own quicksort method.

I think it would make more sense if you stored all your related information together in one array, e.g.:
var array = new[] { Tuple.Create("z", "26", "black"),
Tuple.Create("y", "25", "yellow"),
Tuple.Create("x", "24", "white"),
Tuple.Create("a", "1", "red") };
Then you can sort your array by any key you like and preserving other elements at corresponding positions.

You could approach this by putting all your related strings into a single class, rather than keeping them all in separate arrays.
For example:
public class Demo
{
public string Key;
public string S1;
public string S2;
public override string ToString()
{
return string.Format("Key: {0}, S1: {1}, S2: {2}", Key, S1, S2);
}
}
Then when you want to sort that, you need a way to determine which property or properties to use when comparing elements. There are several ways to do this; one is to make your type implement IComparable<T>.
However there is another more flexible approach. You can supply to your sort method an IComparer<T> object that it can use to compare elements.
Using this, you can "pick out" the member of a class that you want to use when comparing.
Here's a full example:
using System;
using System.Collections.Generic;
namespace Demo
{
public class Demo
{
public string Key;
public string S1;
public string S2;
public override string ToString()
{
return string.Format("Key: {0}, S1: {1}, S2: {2}", Key, S1, S2);
}
}
static class Program
{
static void Main()
{
var list = new List<Demo>
{
new Demo {Key = "Z", S1 = "Z1", S2 = "Z2"},
new Demo {Key = "Y", S1 = "Y1", S2 = "Y2"},
new Demo {Key = "X", S1 = "X1", S2 = "X2"},
new Demo {Key = "W", S1 = "W1", S2 = "W2"},
new Demo {Key = "V", S1 = "V1", S2 = "V2"}
};
// Rather than write your own IComparer<Demo> implementation, you can
// leverage a built-in .Net implementation by using
// Comparer<Demo>.Create() as follows:
var keyComparer = Comparer<Demo>.Create((x, y) => string.Compare(x.Key, y.Key, StringComparison.Ordinal));
QuicksortSTRING(list, 0, list.Count-1, keyComparer);
Console.WriteLine(string.Join("\n", list));
}
public static void QuicksortSTRING<T>(IList<T> elements, int left, int right, IComparer<T> comparer)
{
int i = left, j = right;
var pivot = elements[(left + right)/2];
while (i <= j)
{
while (comparer.Compare(elements[i], pivot) < 0)
{
i++;
}
while (comparer.Compare(elements[j], pivot) > 0)
{
j--;
}
if (i <= j)
{
// Swap
T tmp = elements[i];
elements[i] = elements[j];
elements[j] = tmp;
i++;
j--;
}
}
// Recursive calls
if (left < j)
{
QuicksortSTRING(elements, left, j, comparer);
}
if (i < right)
{
QuicksortSTRING(elements, i, right, comparer);
}
}
}
}

Related

Return null if array element does not exist

Is there a simple^ way of getting the value 'null' if an array element does not exist?
For example, in the code below sArray has 3 elements and the first 3 calls to SomeMethod work (prints true), however the 4th call SomeMethod(sArray[3]); gives me an IndexOutOfRangeException. Is there a way to make the 4th call to SomeMethod print false?
static void Main(string[] args)
{
int[] sArray = new int[]{1,2,3};
SomeMethod(sArray[0]);
SomeMethod(sArray[1]);
SomeMethod(sArray[2]);
SomeMethod(sArray[3]);
}
static void SomeMethod(int? s) => Console.WriteLine(s.HasValue);
^Would prefer single line expression
There is a Linq method ElementAtOrDefault
To use it the way you want to (returning null) you will need ti change the underlying type of your array to nullable int:
int?[] sArray = new int?[]{1,2,3};
SomeMethod(sArray.ElementAtOrDefault(1000));
How about an extension method?
public static T? TryGet<T>(this T[] source, int index) where T: struct
{
if (0 <= index && index < source.Length)
{
return source[index];
}
else
{
return null;
}
}
Then you could write:
static void Main(string[] args)
{
int[] sArray = new int[]{1,2,3};
SomeMethod(sArray.TryGet(0));
SomeMethod(sArray.TryGet(1));
SomeMethod(sArray.TryGet(2));
SomeMethod(sArray.TryGet(3));
}
SomeMethod(sArray.Skip(3).Select(z => (int?)z).FirstOrDefault());
is a working replacement of:
SomeMethod(sArray[3]);
The former will call SomeMethod with null (while the latter will throw an exception if the array doesn't have at least 4 entries).
In Skip(3) the 3 can be changed to whatever index you want to retrieve from the array. The Select is needed to project the int into a int? so that FirstOrDefault returns either the 4th element or null.
If you don't want to use LINQ then you could use:
SomeMethod(sArray.Length > 3 ? sArray[3] : (int?)null);
instead.
Or consider using:
foreach (var entry in sArray.Take(4))
{
SomeMethod(entry);
}
to loop through up to 4 elements of the array (it will work fine if there are fewer than 4 - it will just make fewer calls to SomeMethod).
Arrays in C# have a .Length property which you can check before trying to pass an item from one to SomeMethod, and the typical approach is to loop through each element of the array rather than guessing whether or not an index is valid:
for (int i = 0; i < sArray.Length; i++)
{
SomeMethod(sArray[i]);
}
You will not be able to avoid an IndexOutOfRangeException if you reference an index in an array that doesn't exist.
However, if you really want a method with this type of functionality, you could simply modify your existing code to check whether or not the index specified is greater than the length of the array.
Since your array is an int[] (and not an int?[]), all valid indexes will have a value. Also, we can use the ?. to handle cases where the array itself may be null:
private static void SomeMethod(int[] array, int index) =>
Console.WriteLine(index >= 0 && index < array?.Length);
Then in use, instead of passing an array item with an invalid index (which will always throw an IndexOutOfRangeException), you would pass the array itself and the index separately:
static void Main()
{
int[] sArray = new int[] { 1, 2, 3 };
SomeMethod(sArray, 0);
SomeMethod(sArray, 1);
SomeMethod(sArray, 2);
SomeMethod(sArray, 3);
SomeMethod(null, 0);
GetKeyFromUser("\nPress any key to exit...");
}
Output
in this case I'll suggest you to create a extension somewhere in your code like this
static class ArrExt
{
public static int? Get(this int[] arr, int i)
{
return (i >= 0 && i < arr.Length) ? arr[i] : default(int?);
}
}
then you can do this
int[] sArray = new int[] { 1, 2, 3 };
SomeMethod(sArray.Get(0));
SomeMethod(sArray.Get(1));
SomeMethod(sArray.Get(2));
SomeMethod(sArray.Get(3));
okay this is not a single line solution I know, but it's easier for both programmer and computer.

How to make a sortedlist sort reversely? Do I have to customize a IComparer?

In a sortedlist queue, queue.value[0] gives the corresponding value of a min key. what if i would like to make that it gives the value of a max key?
Do i have to rewrite the icomparer?
Yes you have to rewrite the comparer
example for string as key: (just exchanged x.CompareTo(y) with y.CompareTo(x) )
private class InvertedComparer : IComparer<String>
{
public int Compare(string x, string y)
{
return y.CompareTo(x);
}
}
and the call:
SortedList<string, Object> list = new SortedList<string, Object>(new InvertedComparer());
Here's a link to an article that implements a ReversibleSortedList. This allows for changing the sort direction.
In case you always want reversed sort behavior without the ability to change it on demand, I'd try the constructor that accepts an IComparer.
Just use Array.Reverse() in some simple cases.
using System;
class Program
{
static void Main()
{
// Input array.
int[] array = { 1, 2, 3 };
// Print.
foreach (int value in array)
{
Console.WriteLine(value);
}
Console.WriteLine();
// Reverse.
Array.Reverse(array);
// Print.
foreach (int value in array)
{
Console.WriteLine(value);
}
Console.WriteLine();
// Reverse again.
Array.Reverse(array);
// Print.
foreach (int value in array)
{
Console.WriteLine(value);
}
}
}
* Output *
1
2
3
3
2
1
1
2
3
You could just invert any existing IComparers. Something like:
public static IComparer<T> Invert<T>(this IComparer<T> comparer)
{
return Comparer<T>.Create((x, y) => comparer.Compare(y, x));
}
And use your regular comparer. For e.g.
new SortedList<,>(myShinyComparer.Invert());

How can I use C# to sort values numerically?

I have a string that contains numbers separated by periods. When I sort it appears like this since it is a string: (ascii char order)
3.9.5.2.1.1
3.9.5.2.1.10
3.9.5.2.1.11
3.9.5.2.1.12
3.9.5.2.1.2
3.9.5.2.1.3
3.9.5.2.1.4
etc.
I want it to sort like this: (in numeric order)
3.9.5.2.1.1
3.9.5.2.1.2
3.9.5.2.1.3
...
3.9.5.2.1.9
3.9.5.2.1.10
3.9.5.2.1.11
3.9.5.2.1.12
I know that I can:
Use the Split function to get the individual numbers
Put the values into an object
Sort the object
I prefer to avoid all of that work if it is duplicating existing functionality. Is a method in the .net framework that does this already?
Here's my working solution that also takes care of strings that are not in the right format (e.g. contain text).
The idea is to get the first number within both strings and compare these numbers. If they match, continue with the next number. If they don't, we have a winner. If one if these numbers isn't a number at all, do a string comparison of the part, which wasn't already compared.
It would be easy to make the comparer fully compatible to natural sort order by changing the way to determine the next number.
Look at that.. just found this question.
The Comparer:
class StringNumberComparer : IComparer<string>
{
public int Compare(string x, string y)
{
int compareResult;
int xIndex = 0, yIndex = 0;
int xIndexLast = 0, yIndexLast = 0;
int xNumber, yNumber;
int xLength = x.Length;
int yLength = y.Length;
do
{
bool xHasNextNumber = TryGetNextNumber(x, ref xIndex, out xNumber);
bool yHasNextNumber = TryGetNextNumber(y, ref yIndex, out yNumber);
if (!(xHasNextNumber && yHasNextNumber))
{
// At least one the strings has either no more number or contains non-numeric chars
// In this case do a string comparison of that last part
return x.Substring(xIndexLast).CompareTo(y.Substring(yIndexLast));
}
xIndexLast = xIndex;
yIndexLast = yIndex;
compareResult = xNumber.CompareTo(yNumber);
}
while (compareResult == 0
&& xIndex < xLength
&& yIndex < yLength);
return compareResult;
}
private bool TryGetNextNumber(string text, ref int startIndex, out int number)
{
number = 0;
int pos = text.IndexOf('.', startIndex);
if (pos < 0) pos = text.Length;
if (!int.TryParse(text.Substring(startIndex, pos - startIndex), out number))
return false;
startIndex = pos + 1;
return true;
}
}
Usage:
public static void Main()
{
var comparer = new StringNumberComparer();
List<string> testStrings = new List<string>{
"3.9.5.2.1.1",
"3.9.5.2.1.10",
"3.9.5.2.1.11",
"3.9.test2",
"3.9.test",
"3.9.5.2.1.12",
"3.9.5.2.1.2",
"blabla",
"....",
"3.9.5.2.1.3",
"3.9.5.2.1.4"};
testStrings.Sort(comparer);
DumpArray(testStrings);
Console.Read();
}
private static void DumpArray(List<string> values)
{
foreach (string value in values)
{
Console.WriteLine(value);
}
}
Output:
....
3.9.5.2.1.1
3.9.5.2.1.2
3.9.5.2.1.3
3.9.5.2.1.4
3.9.5.2.1.10
3.9.5.2.1.11
3.9.5.2.1.12
3.9.test
3.9.test2
blabla
No, I don't believe there's anything in the framework which does this automatically. You could write your own IComparer<string> implementation which doesn't do any splitting, but instead iterates over both strings, only comparing as much as is required (i.e. parsing just the first number of each, then continuing if necessary etc) but it would be quite fiddly I suspect. It would also need to make assumptions about how "1.2.3.4.5" compared with "1.3" for example (i.e. where the values contain different numbers of numbers).
Since the comparison you want to do on the strings is different from how strings are normally compared in .Net, you will have to use a custom string string comparer
class MyStringComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// your comparison logic
// split the string using '.' separator
// parse each string item in split array into an int
// compare parsed integers from left to right
}
}
Then you can use the comparer in methods like OrderBy and Sort
var sorted = lst.OrderBy(s => s, new MyStringComparer());
lst.Sort(new MyStringComparer());
This will give you the desired result. If not then just tweak the comparer.
What you are looking for is the natural sort order and Jeff Atwood bloged about it and has links to implementations in different languages. The .NET Framework does not contain an implementation.
Is it possible for you to pad your fields to the same length on the front with 0? If so, then you can just use straight lexicographic sorting on the strings. Otherwise, there is no such method built in to the framework that does this automatically. You'll have to implement your own IComparer<string> if padding is not an option.
Not really, though you may be able to use Regexes or Linq to avoid too much wheel-reinventing. Keep in mind it will cost you much the same computationally to use something built-in as to roll your own.
Try this:
List<string> myList = GetNumberStrings();
myList.Select(s=>s.Split('.')).ToArray().
.Sort((a,b)=>RecursiveCompare(a,b))
.Select(a=>a.Aggregate(new StringBuilder(),
(s,sb)=>sb.Append(s).Append(".")).Remove(sb.Length-1, 1).ToString())
.ToList();
...
public int RecursiveCompare(string[] a, string[] b)
{
return RecursiveCompare(a,b,0)
}
public int RecursiveCompare(string[] a, string[] b, int index)
{
return index == a.Length || index == b.Length
? 0
: a[index] < b[index]
? -1
: a[index] > b[index]
? 1
: RecursiveCompare(a,b, index++);
}
Not the most compact, but it should work and you could use a y-combinator to make the comparison a lambda.
Split each string by '.', iterate through the components and compare them numerically.
This code also assumes that the number of components is signficant (a string '1.1.1' will be greater than '2.1'. This can be adjusted by altering the first if statement in the Compare method below.
int Compare(string a, string b)
{
string[] aParts = a.Split('.');
string[] bParts = b.Split('.');
/// if A has more components than B, it must be larger.
if (aParts.Length != bParts.Length)
return (aParts.Length > bParts.Length) ? 1 : -1;
int result = 0;
/// iterate through each numerical component
for (int i = 0; i < aParts.Length; i++)
if ( (result = int.Parse(aParts[i]).CompareTo(int.Parse(bParts[i]))) !=0 )
return result;
/// all components are equal.
return 0;
}
public string[] sort()
{
/// initialize test data
string l = "3.9.5.2.1.1\n"
+ "3.9.5.2.1.10\n"
+ "3.9.5.2.1.11\n"
+ "3.9.5.2.1.12\n"
+ "3.9.5.2.1.2\n"
+ "3.9.5.2.1.3\n"
+ "3.9.5.2.1.4\n";
/// split the large string into lines
string[] arr = l.Split(new char[] { '\n' },StringSplitOptions.RemoveEmptyEntries);
/// create a list from the array
List<string> strings = new List<string>(arr);
/// sort using our custom sort routine
strings.Sort(Compare);
/// concatenate the list back to an array.
return strings.ToArray();
}
You can use the awesome AlphanumComparator Alphanum natural sort algorithm by David Koelle.
Code:
OrderBy(o => o.MyString, new AlphanumComparator())
If you're gonna use the C# version change it to:
AlphanumComparator : IComparer<string>
and
public int Compare(string x, string y)
In addition to implementing your own IComparer as Jon mentions, if you call ToList() on your array, you can call the .Sort() method and pass in a function parameter that compares two values, as shown here: http://msdn.microsoft.com/en-us/library/w56d4y5z.aspx

How to search a string in String array

I need to search a string in the string array. I dont want to use any for looping in it
string [] arr = {"One","Two","Three"};
string theString = "One"
I need to check whether theString variable is present in arr.
Well, something is going to have to look, and looping is more efficient than recursion (since tail-end recursion isn't fully implemented)... so if you just don't want to loop yourself, then either of:
bool has = arr.Contains(var); // .NET 3.5
or
bool has = Array.IndexOf(arr, var) >= 0;
For info: avoid names like var - this is a keyword in C# 3.0.
Every method, mentioned earlier does looping either internally or externally, so it is not really important how to implement it. Here another example of finding all references of target string
string [] arr = {"One","Two","Three"};
var target = "One";
var results = Array.FindAll(arr, s => s.Equals(target));
Does it have to be a string[] ? A List<String> would give you what you need.
List<String> testing = new List<String>();
testing.Add("One");
testing.Add("Two");
testing.Add("Three");
testing.Add("Mouse");
bool inList = testing.Contains("Mouse");
bool exists = arr.Contains("One");
I think it is better to use Array.Exists than Array.FindAll.
Its pretty simple. I always use this code to search string from a string array
string[] stringArray = { "text1", "text2", "text3", "text4" };
string value = "text3";
int pos = Array.IndexOf(stringArray, value);
if (pos > -1)
{
return true;
}
else
{
return false;
}
If the array is sorted, you can use BinarySearch. This is a O(log n) operation, so it is faster as looping. If you need to apply multiple searches and speed is a concern, you could sort it (or a copy) before using it.
Each class implementing IList has a method Contains(Object value). And so does System.Array.
Why the prohibition "I don't want to use any looping"? That's the most obvious solution. When given the chance to be obvious, take it!
Note that calls like arr.Contains(...) are still going to loop, it just won't be you who has written the loop.
Have you considered an alternate representation that's more amenable to searching?
A good Set implementation would perform well. (HashSet, TreeSet or the local equivalent).
If you can be sure that arr is sorted, you could use binary search (which would need to recurse or loop, but not as often as a straight linear search).
You can use Find method of Array type. From .NET 3.5 and higher.
public static T Find<T>(
T[] array,
Predicate<T> match
)
Here is some examples:
// we search an array of strings for a name containing the letter “a”:
static void Main()
{
string[] names = { "Rodney", "Jack", "Jill" };
string match = Array.Find (names, ContainsA);
Console.WriteLine (match); // Jack
}
static bool ContainsA (string name) { return name.Contains ("a"); }
Here’s the same code shortened with an anonymous method:
string[] names = { "Rodney", "Jack", "Jill" };
string match = Array.Find (names, delegate (string name)
{ return name.Contains ("a"); } ); // Jack
A lambda expression shortens it further:
string[] names = { "Rodney", "Jack", "Jill" };
string match = Array.Find (names, n => n.Contains ("a")); // Jack
At first shot, I could come up with something like this (but it's pseudo code and assuming you cannot use any .NET built-in libaries). Might require a bit of tweaking and re-thinking, but should be good enough for a head-start, maybe?
int findString(String var, String[] stringArray, int currentIndex, int stringMaxIndex)
{
if currentIndex > stringMaxIndex
return (-stringMaxIndex-1);
else if var==arr[currentIndex] //or use any string comparison op or function
return 0;
else
return findString(var, stringArray, currentIndex++, stringMaxIndex) + 1 ;
}
//calling code
int index = findString(var, arr, 0, getMaxIndex(arr));
if index == -1 printOnScreen("Not found");
else printOnScreen("Found on index: " + index);
In C#, if you can use an ArrayList, you can use the Contains method, which returns a boolean:
if MyArrayList.Contains("One")
You can check the element existence by
arr.Any(x => x == "One")
it is old one ,but this is the way i do it ,
enter code herevar result = Array.Find(names, element => element == "One");
I'm surprised that no one suggested using Array.IndexOf Method.
Indeed, Array.IndexOf has two advantages :
It allows searching if an element is included into an array,
It gets at the same time the index into the array.
int stringIndex = Array.IndexOf(arr, theString);
if (stringIndex >= 0)
{
// theString has been found
}
Inline version :
if (Array.IndexOf(arr, theString) >= 0)
{
// theString has been found
}
Using Contains()
string [] SomeArray = {"One","Two","Three"};
bool IsExist = SomeArray.Contains("One");
Console.WriteLine("Is string exist: "+ IsExist);
Using Find()
string [] SomeArray = {"One","Two","Three"};
var result = Array.Find(SomeArray, element => element == "One");
Console.WriteLine("Required string is: "+ result);
Another simple & traditional way, very useful for beginners to build logic.
string [] SomeArray = {"One","Two","Three"};
foreach (string value in SomeArray) {
if (value == "One") {
Console.WriteLine("Required string is: "+ value);
}
}

Order an Array like another Array in C#

What is the best algorithm to take array like below:
A {0,1,2,3}
I expected to order it like array below:
B {3,1,0,2}
Any ideas?
So if you have two arrays and they hold the same data just in different order then just do this:
A = B
I suspect that is not your situation so I think we need more info.
What you need to do is determine the ordering of B and then apply that ordering to A. One way to accomplish this is to undo the ordering of B and keep track of what happens along the way. Then you can do the reverse to A.
Here's some sketchy C# (sorry, I haven't actually run this)...
Take a copy of B:
List<int> B2 = new List<int>(B);
Now sort it, using a sort function that records the swaps:
List<KeyValuePair<int,int>> swaps = new List<KeyValuePair<int,int>>();
B2.Sort( delegate( int x, int y ) {
if( x<y ) return -1;
if( x==y ) return 0;
// x and y must be transposed, so assume they will be:
swaps.Add( new KeyValuePair<int,int>(x,y) );
return 1;
});
Now apply the swaps, in reverse order, to A:
swaps.Reverse();
foreach( KeyValuePair<int,int> x in swaps )
{
int t = A[x.key];
A[x.key] = A[x.value];
A[x.value] = t;
}
Depending how the built-in sort algorithm works, you might need to roll your own. Something nondestructive like a merge sort should give you the correct results.
Here's my implementation of the comparer (uses LINQ, but can be easily adapted to older .net versions). You can use it for any sorting algorithms such as Array.Sort, Enumerable.OrderBy, List.Sort, etc.
var data = new[] { 1, 2, 3, 4, 5 };
var customOrder = new[] { 2, 1 };
Array.Sort(data, new CustomOrderComparer<int>(customOrder));
foreach (var v in data)
Console.Write("{0},", v);
The result is 2,1,3,4,5, - any items not listed in the customOrder are placed at the end in the default for the given type (unless a fallback comparator is given)
public class CustomOrderComparer<TValue> : IComparer<TValue>
{
private readonly IComparer<TValue> _fallbackComparer;
private const int UseDictionaryWhenBigger = 64; // todo - adjust
private readonly IList<TValue> _customOrder;
private readonly Dictionary<TValue, uint> _customOrderDict;
public CustomOrderComparer(IList<TValue> customOrder, IComparer<TValue> fallbackComparer = null)
{
if (customOrder == null) throw new ArgumentNullException("customOrder");
_fallbackComparer = fallbackComparer ?? Comparer<TValue>.Default;
if (UseDictionaryWhenBigger < customOrder.Count)
{
_customOrderDict = new Dictionary<TValue, uint>(customOrder.Count);
for (int i = 0; i < customOrder.Count; i++)
_customOrderDict.Add(customOrder[i], (uint) i);
}
else
_customOrder = customOrder;
}
#region IComparer<TValue> Members
public int Compare(TValue x, TValue y)
{
uint indX, indY;
if (_customOrderDict != null)
{
if (!_customOrderDict.TryGetValue(x, out indX)) indX = uint.MaxValue;
if (!_customOrderDict.TryGetValue(y, out indY)) indY = uint.MaxValue;
}
else
{
// (uint)-1 == uint.MaxValue
indX = (uint) _customOrder.IndexOf(x);
indY = (uint) _customOrder.IndexOf(y);
}
if (indX == uint.MaxValue && indY == uint.MaxValue)
return _fallbackComparer.Compare(x, y);
return indX.CompareTo(indY);
}
#endregion
}
In the example you gave (an array of numbers), there would be no point in re-ordering A, since you could just use B.
So, presumably these are arrays of objects which you want ordered by one of their properties.
Then, you will need a way to look up items in A based on the property in question (like a hashtable). Then you can iterate B (which is in the desired sequence), and operate on the corresponding element in A.
Both array's contain the same values (or nearly so) but I need to force them to be in the same order. For example, in array A the value "3045" is in index position 4 and in array B it is in index position 1. I want to reorder B so that the index positions of like values are the same as A.
If they are nearly the same then here is some pseudo code:
Make an ArrayList
Copy the contents of the smaller array to the arraylist
for each item I in the larger array
FInd I in the ArrayList
Append I to a new array
Remove I from the arraylist
Could the issue be resolved using a Dictionary so the elements have a relationship that isn't predicated on sort order at all?

Categories