I currently have a list of coordinates that I need sorted. Each line represents Longitude, Latitude. I need to sort only on the Longitude.
It is stored in an string array:
string[] coords = fpdp.Coordinates.ToArray();
Here is the original list:
**LongLat**
98.63,85.02
43.08,79.07
26.97,70.88
18.8,62.3
13.47,53.5
8.57,44.8
3.58,36.35
-1.63,28.2
-6.93,20.33
-12.12,12.63
-17.17,5.02
-22.63,-2.25
-28.22,-9.43
-34.98,-15.7
-42.67,-21.08
-51.18,-25.62
-60.55,-29.12
-70.7,-31.12
-81.2,-31.18
-91.42,-29.72
-101.02,-26.97
-109.62,-22.85
-117.3,-17.83
-123.9,-11.9
-129.32,-5.05
-133.55,2.47
-136.9,10.3
-140.45,17.78
-144.75,24.98
-148.6,32.53
-152.02,40.37
-155.85,48.28
-160.8,56.27
-165.75,64.48
-172.62,72.78
171.35,80.83
98.93,85.17
Here is what I need it to look like. It is sorted by Large to small for positive numbers, and small to large for negative numbers. Only focusing on the first longitude coordinate:
**LongLat-Sorted**
171.35,80.83
98.93,85.17
98.63,85.02
43.08,79.07
26.97,70.88
18.8,62.3
13.47,53.5
8.57,44.8
3.58,36.35
-1.63,28.2
-6.93,20.33
-12.12,12.63
-17.17,5.02
-22.63,-2.25
-28.22,-9.43
-34.98,-15.7
-42.67,-21.08
-51.18,-25.62
-60.55,-29.12
-70.7,-31.12
-81.2,-31.18
-91.42,-29.72
-101.02,-26.97
-109.62,-22.85
-117.3,-17.83
-123.9,-11.9
-129.32,-5.05
-133.55,2.47
-136.9,10.3
-140.45,17.78
-144.75,24.98
-148.6,32.53
-152.02,40.37
-155.85,48.28
-160.8,56.27
-165.75,64.48
-172.62,72.78
How can I accomplish this in code? Any help would be great.
SOLUTION:
I tweaked this to the following, and it's working. Thanks a lot! :)
public class LongLatSort : IComparer
{
int IComparer.Compare(Object x, Object y)
{
string[] longLatParts1 = Convert.ToString(x).Split(',');
string[] longLatParts2 = Convert.ToString(y).Split(',');
var var1 = double.Parse(longLatParts1[0]);
var var2 = double.Parse(longLatParts2[0]);
if (var1 > var2)
{
return -1; // flipped for descending
}
else if (var1 < var2)
{
return 1; // flipped for descending
}
// secondary sort on latitude when values are equal
return var1 > var2 ? -1 : 1; // flipped for descending
}
}
Just finished tested this, seems to work.
class SimplePoint
{
public SimplePoint(string coord)
{
var coords = coord.Split(',').Select(s => double.Parse(s, System.Globalization.CultureInfo.InvariantCulture)).ToArray();
X = coords[0];
Y = coords[1];
}
public double X;
public double Y;
public override string ToString()
{
return X.ToString() + "," + Y.ToString();
}
}
static class LongLatParseAndSort
{
public static string SortedLongLat(string unsorted)
{
return unsorted
.Split(' ')
.Select(c => new SimplePoint(c))
.OrderByDescending(sp => sp.X)
.Select(sp => sp.ToString())
.Aggregate((a, b) => a += b);
}
}
How is this data stored? An array of Strings? or a 2-dimensional array or floats? or an Array of some structure with a lat and long? I'll assume its an array of LongLat since thats how you worded it.
EDIT I realized your subject title said string, so I added a constructor to convert from string to a LongLat.
Your desired result looks sorted descending on Longitude.
This code is untested, forgive me if it's not 100% but you get the idea.
// This is pretending to be the data structure you are using
public class LongLat {
private float mLongitude;
private float mLatitude;
// constructor from string for convenience
public LongLat(string longLatString ) {
string[] longLatParts = longLatString.Split(',');
mLongitude = float.Parse(longLatParts[0]);
mLatitude = float.Parse(longLatParts[1]);
}
public float Longitude {get; set; }
public float Latitude {get; set; }
}
// The sorter
public class LongLatSort : IComparer {
public int IComparer.Compare(object a, object b) {
LongLat o1=(LongLat)a;
LongLat o2=(LongLat)b;
if (o1.Longitude > o2.Longitude) {
return -1; // flipped for descending
} else if ( o1.Latitude < o2.Longitude ) {
return 1; // flipped for descending
}
// secondary sort on latitude when values are equal
return o1.Latitude > o2.Latitude ? -1 : 1; // flipped for descending
}
}
// now you should be able to use the sorter something like this?
// though best to not instantiate the Comparer every time but you get the idea
// EDIT: create your array of LongLats from strings first
Arrays.Sort( yourArrayofLongLats, new LongLastSort() );
Related
I am writing a function that calculates distance from x and y coordinate. I have a two dimensional array that has bunch of x and y coordinates.
Function returns a list of x and y coordinates in the order of distance from a point. To calculate the distance from each point there is a formula(Square root of sum of coordinates).
I can calculate the distance for each x and y coordinate. I am adding that to list. How do I store distance as another property as it is for that particular coordinate and then sort it.
public static List<List<int>> calculateDistance(int[,] Coordinates)
{
List<List<int>> result = new List<List<int>>();
int bound0 = Coordinates.GetUpperBound(0);
List<double> distance = new List<double>();
for (int i = 0;i <= bound0; i++)
{
distance.Add(Math.Sqrt(Coordinates[i, 0]) + Coordinates[i,1]));
}
return result;
}
}
Based on your description you don't need to create a calculateDistance method. Formula to calculate distance can be given with lambda expression. Anywhere in your code you can create the list you need and get it sorted with Linq.
Example
var list = Enumerable
.Range(0, Coordinates.GetLength(0))
.Select(i => new { X = Coordinates[i, 0], Y = Coordinates[i, 1], D = Math.Sqrt(Math.Pow(Coordinates[i, 0], 2) + Math.Pow(Coordinates[i, 1], 2)) });
Math.Sqrt(Math.Pow(Coordinates[i, 0], 2) + Math.Pow(Coordinates[i, 1], 2)) is used here for demonstration. Instead use your own expression to calculate the distance.
To sort this you can simply use
var list2 = list.OrderBy(a => a.D);
It would be more helpful if you try to define proper data structures for your inputs and your output. It could be something as simple as a tuple, or something more idiomatic like a struct or a class.
struct Coordinate {
public Coordinate(int x, int y) {
X = x;
Y = y;
}
public int X { get; }
public int Y { get; }
}
Then define a result structure, something like:
struct Result {
public Result(Coordinate coordinate, double distance) {
Coordinate = coordinate;
Distance = distance;
}
public Coordinate Coordinate { get; }
public double Distance { get; }
}
Then you can create a list of those result items like:
public List<Result> ComputeDistances(List<Coordinate> coordinates) {
List<Result> results = new List<Result>();
foreach (var coordinate in coordinates) {
double distance = Math.Sqrt(coordinate.X + coordinate.Y); // *
results.Add(new Result(coordinate, distance));
}
return results;
}
(*) Note that the specified distance function is a little bit odd. Normally you would like to sum the squares of the X and Y coordinate
If you like a more functional style, you can change that code quite a bit:
public IEnumerable<Result> ComputeDistances(IEnumerable<Coordinate> coordinates) {
return from coordinate in coordinates
let distance = Math.Sqrt(coordinate.X + coordinate.Y)
select new Result(coordinate, distance);
}
Changing from List to IEnumerable allows you with the proper care, to delay the execution of the computation.
After you have the sequence of results, the easiest way to sort them is using the OrderBy extension method.
public IEnumerable<Result> SortByDistance(IEnumerable<Result> results)
{
return results.OrderBy(result => result.Distance);
}
And then combine all:
List<Coordinate> coordinates = .... // get the list of coordinates somehow;
IEnumerable<Result> distances = ComputeDistances(coordinates);
IEnumerable<Result> sortedByDistance = SortByDistance(distances);
// if you want to get back a list, in order to avoid enumerating multiple times
List<Result> results = sortedByDistance.ToList();
I am trying to sort a list using IComparer there are about 20,000 items in my list. The first approximately 100 and last 100 items are sorted but the center is not. I am not sure what I am doing wrong. Attached is the parts of the code that does the sort.
class myclass:me
{
private void mysort()
{
// Sort the data
DistComparer dc = new DistComparer();
st.Sort(dc);
}
}
class DistComparer : IComparer<Tower>
{
public int Compare(st x, st y)
{
double dist1 = getDistance(new PointF(45.0f, -80f), new PointF(x.Lat, x.Long));
double dist2 = getDistance(new PointF(45.0f, -80f), new PointF(y.Lat, y.Long));
if (dist1 > dist2)
return 1;
else if (dist1 == dist2)
return 0;
else
return -1;
}
private static double getDistance(PointF pt1, PointF pt2)
{
return Math.Sqrt(Math.Pow((pt1.X - pt2.X), 2) + Math.Pow((pt1.Y - pt2.Y), 2));
}
}
}
I have a double[] array, i want to use it as key (not literally, but in the way that the key is matched when all the doubles in the double array need to be matched)
What is the fastest way to use the double[] array as key to dictionary?
Is it using
Dictionary<string, string> (convert double[] to a string)
or
anything else like converting it
Given that all key arrays will have the same length, either consider using a Tuple<,,, ... ,>, or use a structural equality comparer on the arrays.
With tuple:
var yourDidt = new Dictionary<Tuple<double, double, double>, string>();
yourDict.Add(Tuple.Create(3.14, 2.718, double.NaN), "da value");
string read = yourDict[Tuple.Create(3.14, 2.718, double.NaN)];
With (strongly typed version of) StructuralEqualityComparer:
class DoubleArrayStructuralEqualityComparer : EqualityComparer<double[]>
{
public override bool Equals(double[] x, double[] y)
{
return System.Collections.StructuralComparisons.StructuralEqualityComparer
.Equals(x, y);
}
public override int GetHashCode(double[] obj)
{
return System.Collections.StructuralComparisons.StructuralEqualityComparer
.GetHashCode(obj);
}
}
...
var yourDict = new Dictionary<double[], string>(
new DoubleArrayStructuralEqualityComparer());
yourDict.Add(new[] { 3.14, 2.718, double.NaN, }, "da value");
string read = yourDict[new[] { 3.14, 2.718, double.NaN, }];
Also consider the suggestion by Sergey Berezovskiy to create a custom class or (immutable!) struct to hold your set of doubles. In that way you can name your type and its members in a natural way that makes it more clear what you do. And your class/struct can easily be extended later on, if needed.
Thus all arrays have same length and each item in array have specific meaning, then create class which holds all items as properties with descriptive names. E.g. instead of double array with two items you can have class Point with properties X and Y. Then override Equals and GetHashCode of this class and use it as key (see What is the best algorithm for an overriding GetHashCode):
Dictionary<Point, string>
Benefits - instead of having array, you have data structure which makes its purpose clear. Instead of referencing items by indexes, you have nice named property names, which also make their purpose clear. And also speed - calculating hash code is fast. Compare:
double[] a = new [] { 12.5, 42 };
// getting first coordinate a[0];
Point a = new Point { X = 12.5, Y = 42 };
// getting first coordinate a.X
[Do not consider this a separate answer; this is an extension of #JeppeStigNielsen's answer]
I'd just like to point out that you make Jeppe's approach generic as follows:
public class StructuralEqualityComparer<T>: IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
return StructuralComparisons.StructuralEqualityComparer.Equals(x, y);
}
public int GetHashCode(T obj)
{
return StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
}
public static StructuralEqualityComparer<T> Default
{
get
{
StructuralEqualityComparer<T> comparer = _defaultComparer;
if (comparer == null)
{
comparer = new StructuralEqualityComparer<T>();
_defaultComparer = comparer;
}
return comparer;
}
}
private static StructuralEqualityComparer<T> _defaultComparer;
}
(From an original answer here: https://stackoverflow.com/a/5601068/106159)
Then you would declare the dictionary like this:
var yourDict = new Dictionary<double[], string>(new StructuralEqualityComparer<double[]>());
Note: It might be better to initialise _defaultComparer using Lazy<T>.
[EDIT]
It's possible that this might be faster; worth a try:
class DoubleArrayComparer: IEqualityComparer<double[]>
{
public bool Equals(double[] x, double[] y)
{
if (x == y)
return true;
if (x == null || y == null)
return false;
if (x.Length != y.Length)
return false;
for (int i = 0; i < x.Length; ++i)
if (x[i] != y[i])
return false;
return true;
}
public int GetHashCode(double[] data)
{
if (data == null)
return 0;
int result = 17;
foreach (var value in data)
result += result*23 + value.GetHashCode();
return result;
}
}
...
var yourDict = new Dictionary<double[], string>(new DoubleArrayComparer());
Ok this is what I found so far:
I input an entry (length 4 arrray) to the dictionary, and access it for 999999 times on my machine:
Dictionary<double[], string>(
new DoubleArrayStructuralEqualityComparer()); takes 1.75 seconds
Dictionary<Tuple<double...>,string> takes 0.85 seconds
The code below takes 0.1755285 seconds, which is the fastest now! (in line with the comment with Sergey.)
The fastest - The code of DoubleArrayComparer by Matthew Watson takes 0.15 seconds!
public class DoubleArray
{
private double[] d = null;
public DoubleArray(double[] d)
{
this.d = d;
}
public override bool Equals(object obj)
{
if (!(obj is DoubleArray)) return false;
DoubleArray dobj = (DoubleArray)obj;
if (dobj.d.Length != d.Length) return false;
for (int i = 0; i < d.Length; i++)
{
if (dobj.d[i] != d[i]) return false;
}
return true;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
for (int i = 0; i < d.Length;i++ )
{
hash = hash*23 + d[i].GetHashCode();
}
return hash;
}
}
}
I'm new to C#, have looked at numerous posts but am still confused.
I have a array list:
List<Array> moves = new List<Array>();
I'm adding moves to it using the following:
string[] newmove = { piece, axis.ToString(), direction.ToString() };
moves.Add(newmove);
And now I wish to remove duplicates using the following:
moves = moves.Distinct();
However it's not letting me do it. I get this error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
Help please? I'd be so grateful.
Steve
You need to call .ToList() after the .Distinct method as it returns IEnumerable<T>. I would also recommend you using a strongly typed List<string[]> instead of List<Array>:
List<string[]> moves = new List<string[]>();
string[] newmove = { piece, axis.ToString(), direction.ToString() };
moves.Add(newmove);
moves.Add(newmove);
moves = moves.Distinct().ToList();
// At this stage moves.Count = 1
Your code has two errors. The first is the missing call to ToList, as already pointed out. The second is subtle. Unique compares objects by identity, but your duplicate list items have are different array instances.
There are multiple solutions for that problem.
Use a custom equality comparer in moves.Distinct().ToList(). No further changes necessary.
Sample implementation:
class ArrayEqualityComparer<T> : EqualityComparer<T> {
public override bool Equals(T[] x, T[] y) {
if ( x == null ) return y == null;
else if ( y == null ) return false;
return x.SequenceEquals(y);
}
public override int GetHashCode(T[] obj) {
if ( obj == null) return 0;
return obj.Aggregate(0, (hash, x) => hash ^ x.GetHashCode());
}
}
Filtering for unique items:
moves = moves.Distinct(new ArrayEqualityComparer<string>()).ToList();
Use Tuple<string,string,string> instead of string[]. Tuple offers built-in structural equality and comparison. This variant might make your code cluttered because of the long type name.
Instantiation:
List<Tuple<string, string, string>> moves =
new List<Tuple<string, string, string>>();
Adding new moves:
Tuple<string, string, string> newmove =
Tuple.Create(piece, axis.ToString(), direction.ToString());
moves.Add(newmove);
Filtering for unique items:
moves = moves.Distinct().ToList();
Use a custom class to hold your three values. I'd actually recommend this variant, because it makes all your code dealing with moves much more readable.
Sample implementation:
class Move {
public Move(string piece, string axis, string direction) {
Piece = piece;
Axis = axis;
Direction = direction;
}
string Piece { get; private set; }
string Axis { get; private set; }
string Direction { get; private set; }
public override Equals(object obj) {
Move other = obj as Move;
if ( other != null )
return Piece == other.Piece &&
Axis == other.Axis &&
Direction == other.Direction;
return false;
}
public override GetHashCode() {
return Piece.GetHashCode() ^
Axis.GetHashCode() ^
Direction.GetHashCode();
}
// TODO: override ToString() as well
}
Instantiation:
List<Move> moves = new List<Move>();
Adding new moves:
Move newmove = new Move(piece, axis.ToString(), direction.ToString());
moves.Add(newmove);
Filtering for unique items:
moves = moves.Distinct().ToList();
The compiler error is because you need to convert the result to a list:
moves = moves.Distinct().ToList();
However it probably won't work as you want, because arrays don't have Equals defined in the way that you are hoping (it compares the references of the array objects, not the values inside the array). Instead of using an array, create a class to hold your data and define Equals and GetHashCode to compare the values.
Old question, but this is an O(n) solution using O(1) additional space:
public static void RemoveDuplicates(string[] array)
{
int c = 0;
int i = -1;
for (int n = 1; n < array.Length; n++)
{
if (array[c] == array[n])
{
if (i == -1)
{
i = n;
}
}
else
{
if (i == -1)
{
c++;
}
else
{
array[i] = array[n];
c++;
i++;
}
}
}
}
I have an ArrayList that contains a large number of strings. It needs to be sorted in place based on three fields (essentially three substrings) which are Name, Age and Amt. Age is the first substring (position 0-3), Name is second (3-6) and Amt is last (6-10). The order in which these parameters are to be sorted is very important and is as follows:
First perform ascending sort by Name THEN do ascending sort by Age (which actually comes earlier in the substring) and THEN do descending sort by Amt. That's it.
I have this class
public class ArrComparer : IComparer
{
public int Compare(object x, object y)
{
string left = x.ToString();
string right = y.ToString();
string lhs = left.Substring(3, 6);
string rhs = right.Substring(3, 6);
return lhs.CompareTo(rhs);
}
}
which I use to sort based on just one field - Name by invoking
RecordList.Sort(new ArrComparer());
This lets me sort correctly based on that one field. The question is how can I modify this code to allow me to sort based on all three AT ONCE, in the right order and using proper asc/desc mode?
Any code or tips would be greatly appreciated. (By the way, in case you are wondering using generic List<T> is not an option in this project).
ArrayList still implements IEnumerable, meaning you can use the simple orderby() and thenby() extensions in linq:
RecordList = new ArrayList(
RecordList.Cast<string>().OrderBy(s => s.Substring(3,3))
.ThenBy(s => int.Parse(s.Substring(0,3)))
.ThenByDescending(s => double.Parse(s.Substring(6,4)))
.ToArray());
Other ways to express this include building a more complicated .OrderBy() or using an anonymous type to compose your string as an object:
RecordList = new ArrayList(
Record.Cast<string>().Select(s => new {source = s, age = int.Parse(s.Substring(0, 3)), name = s.Substring(3,3), amt = double.Parse(s.Substring(6,4))})
.OrderBy(o => o.name)
.ThenBy(o => o.age)
.ThenByDescending(o => o.amt)
.Select(o => o.source).ToArray());
I like that option because it sets you up to start thinking in terms objects. Play your cards right and you can skip that last .Select() projection to keep the objects rather than going back to strings, which will save the work of having to do all that parsing over again later.
If these aren't an option (possibly for the same reason you can't use List<T>), it's easy to modify your existing compare method like so:
public class ArrComparer : IComparer
{
public int Compare(object x, object y)
{
int result;
string left = x.ToString();
string right = y.ToString();
string lhs1 = left.Substring(3, 3);
string rhs1 = right.Substring(3, 3);
result = lhs1.CompareTo(rhs1);
if (result == 0)
{
int lhs2 = int.Parse(left.Substring(0,3));
int rhs2 = int.Parse(right.Substring(0,3));
result = lhs2.CompareTo(rhs2);
}
if (result == 0)
{
double lhs3 = double.Parse(left.Substring(6,4));
double rhs3 = double.Parse(right.Substring(6,4));
result = rhs3.CompareTo(lhs3);
}
return result;
}
}
You can compare part by part:
string left = (string)x;
string right = (string)y;
string lname = left.Substring(3, 3);
string rname = right.Substring(3, 3);
int result = lname.CompareTo(rname);
if (result != 0) return result;
string lage = left.Substring(0, 3);
string rage = right.Substring(0, 3);
int result = lage.CompareTo(rage);
if (result != 0) return result;
string lamt = left.Substring(6);
string ramt = right.Substring(6);
return -lamt.CompareTo(ramt);
If you need an IComparer, try something like:
public class ArrComparer : IComparer
{
public int Compare(object x, object y)
{
string left = x.ToString();
string right = y.ToString();
string leftName = left.Substring([whatever]);
string rightName = right.Substring([whatever]);
// First try comparing names
int result = leftName.CompareTo(rightName);
if (result != 0)
{
return result;
}
// If that didn't work, compare ages
string leftAge = left.Substring([whatever]);
string rightAge = right.Substring([whatever]);
result = leftAge.CompareTo(rightAge);
if (result != 0)
{
return result;
}
// Finally compare amounts (descending)
string leftAmt = left.Substring([whatever]);
string rightAmt = right.Substring([whatever]);
result = -leftAmt.CompareTo(rightAmt); // Minus for descending
return result;
}
}
I would recommend storing your records in an object, and make those comparable instead.
In order to compare all three fields using the same method you are currently using you simply need to extract all three pieces of data and do a full comparison.
public class ArrComparer : IComparer
{
public int Compare(object x, object y)
{
string left = x.ToString();
string right = y.ToString();
// Note I assumed indexes since yours were overlapping.
string lage = left.Substring(0, 3);
string lname = left.Substring(3, 3);
string lamt = left.Substring(7, 3);
string rage = left.Substring(0, 3);
string rname = left.Substring(3, 3);
string ramt = left.Substring(7, 3);
// Compare name first, if one is greater return
int result = lname.CompareTo(rname);
if (result != 0)
return result;
// else compare age, if one is greater return
result = lage.CompareTo(rage)
if (result != 0)
return result;
// else compare amt if one is greater return
result = lamt.CompareTo(ramt)
if (result != 0)
return result;
// else they are equal
return 0;
}
}
you could expend your ArrCompare with if statements like if(rhs == lhs) compere with other part of string.
Accen deccend is meeter of return -1 or 1