Order Text as Numbers

  • Thread starter Thread starter Dave
  • Start date Start date
D

Dave

Hi,

I am currently writing a requirements gathering system which which
have requirement values of 1.1, 1.1.2, 1.1.3, 2.3.4.5 etc etc.

This works fine stored as varchar in SQL Server 2000 until I want to
sort it in a .Net datagrid. As soon as I reach 10, it obviously puts
the record down with the 1's and I am guessing 20 will go with the 2's
etc etc.

Can anyone suggest a way of getting round this - I know it is because
of the details being stored as text but I need some sort of solution.

Thanks
 
Hi Dave,

You could create a struct out of a single string for the text and have the struct implement IComparable. Putting the structs inside any list with a Sort method should sort it according to your needs.

The CompareTo method will be called on each object to determine if it is "larger" or "smaller" or "equal" to some other object.

ArrayList list = new ArrayList();

RequirementNumber r = new RequirementNumber();
r.Number = "1.1";
list.Add(r);

r = new RequirementNumber();
r.Number = "1.10";
list.Add(r);

r = new RequirementNumber();
r.Number = "1.2";
list.Add(r);

r = new RequirementNumber();
r.Number = "1.1.1";
list.Add(r);

list.Sort();


The list will now be

1.1
1.1.1
1.2
1.10

If this is not the way you want it sorted, just changed the code to what you need.

public struct RequirementNumber : IComparable
{
public string Number;

public int CompareTo(object o) // this method will be called by Sort()
{
if(!(o is RequirementNumber))
return -1;

RequirementNumber temp = (RequirementNumber)o;

string[] local = Number.Split(new char[]{'.'});
string[] remote = temp.Number.Split(new char[]{'.'});

int[] numlocal = new int[local.Length];
int[] numremote = new int[remote.Length];

for(int i = 0; i < local.Length; i++)
{
string s = (string)local;
try
{
numlocal = Int32.Parse(s);
}
catch
{
numlocal = 0;
}
}

for(int i = 0; i < remote.Length; i++)
{
string s = (string)remote;
try
{
numremote = Int32.Parse(s);
}
catch
{
numremote = 0;
}
}

for(int i = 0; i < numlocal.Length; i++)
{
if(numlocal < numremote) // X.1.X < X.2.X
return -1;
else if(numlocal > numremote) // >
return 1;
// else equal and we need to check the next step
if(i == numremote.Length -1) // X.X > X.X.X
return 1;
}

if(numremote.Length > numlocal.Length) // X.X.X < X.X
return -1;

return 0; // numbers are equal
}
}


Happy coding!
Morten Wennevik [C# MVP]
 
Hi,

I think there is a better way of doing this, instead of implementing a
struct that needs to be carry out with the data just create a class that
implement IComparer. Then you can use Array.Sort with this IComparer.

This is how it will looks:
public class NumberingSorter: IComparer
{


public int Compare(object x, object y)
{
if ( (x== null ) && (y==null) )
return 0;
if ( x == null )
return -1;
if ( y == null )
return 1;
// convert to string
string x1 = x.ToString();
string y1 = y.ToString();
//now split the str
string[] partsX = x1.Split( new char[] { '.' } );
string[] partsY = y1.Split( new char[] { '.' } );

//You should put this on a cycle !!!
if ( partsX[0] > partsY[0] )
return 1;

if ( partsX[0] < partsY[0] )
return -1;

// If they are equal pass to the next one
}

}

Please note that I adapted the above code from another class with a similar
functionality and you will have to refine it, the logic is there though.

Cheers,

--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation

Morten Wennevik said:
Hi Dave,

You could create a struct out of a single string for the text and have the
struct implement IComparable. Putting the structs inside any list with a
Sort method should sort it according to your needs.
The CompareTo method will be called on each object to determine if it is
"larger" or "smaller" or "equal" to some other object.
ArrayList list = new ArrayList();

RequirementNumber r = new RequirementNumber();
r.Number = "1.1";
list.Add(r);

r = new RequirementNumber();
r.Number = "1.10";
list.Add(r);

r = new RequirementNumber();
r.Number = "1.2";
list.Add(r);

r = new RequirementNumber();
r.Number = "1.1.1";
list.Add(r);

list.Sort();


The list will now be

1.1
1.1.1
1.2
1.10

If this is not the way you want it sorted, just changed the code to what you need.

public struct RequirementNumber : IComparable
{
public string Number;

public int CompareTo(object o) // this method will be called by Sort()
{
if(!(o is RequirementNumber))
return -1;

RequirementNumber temp = (RequirementNumber)o;

string[] local = Number.Split(new char[]{'.'});
string[] remote = temp.Number.Split(new char[]{'.'});

int[] numlocal = new int[local.Length];
int[] numremote = new int[remote.Length];

for(int i = 0; i < local.Length; i++)
{
string s = (string)local;
try
{
numlocal = Int32.Parse(s);
}
catch
{
numlocal = 0;
}
}

for(int i = 0; i < remote.Length; i++)
{
string s = (string)remote;
try
{
numremote = Int32.Parse(s);
}
catch
{
numremote = 0;
}
}

for(int i = 0; i < numlocal.Length; i++)
{
if(numlocal < numremote) // X.1.X < X.2.X
return -1;
else if(numlocal > numremote) // >
return 1;
// else equal and we need to check the next step
if(i == numremote.Length -1) // X.X > X.X.X
return 1;
}

if(numremote.Length > numlocal.Length) // X.X.X < X.X
return -1;

return 0; // numbers are equal
}
}


Happy coding!
Morten Wennevik [C# MVP]
 
assuming there are never more than 99 sub-requirements for a single
requirement, you can store them in the database as System.Decimal:
1.1 -> 1.01
1.1.2 -> 1.0102
1.1.3 -> 1.0103
2.3.4.5 -> 2.030405

etc.
then they will be sorted correctly.
 
Yes Ignacio, Array.Sort with IComparer is probably far better. You learn something every day :)

In this case, drop the struct, make a new class and use an instance of the class to sort an array of strings.

string[] test = new string[]{"1.1", "1.10", "1.2", "1.1.1"};
SortClass sc = new SortClass();
Array.Sort(test, 0, test.Length, sc);

This should sort the strings in the same way as my other example.

public class SortClass : IComparer
{
public int Compare(object x, object y)
{
if(!(x is String) || !(y is string))
return -1;

string X = (string)x;
string Y = (string)y;

string[] local = X.Split(new char[]{'.'});
string[] remote = Y.Split(new char[]{'.'});

int[] numlocal = new int[local.Length];
int[] numremote = new int[remote.Length];

for(int i = 0; i < local.Length; i++)
{
string s = (string)local;
try
{
numlocal = Int32.Parse(s);
}
catch
{
numlocal = 0;
}
}

for(int i = 0; i < remote.Length; i++)
{
string s = (string)remote;
try
{
numremote = Int32.Parse(s);
}
catch
{
numremote = 0;
}
}

for(int i = 0; i < numlocal.Length; i++)
{
if(numlocal < numremote) // X.1.X < X.2.X
return -1;
else if(numlocal > numremote) // >
return 1;
// else equal and we need to check the next step
if(i == numremote.Length -1) // X.X > X.X.X
return 1;
}

if(numremote.Length > numlocal.Length) // X.X.X < X.X
return -1;
return 0; // numbers are equal
}
}


Happy coding!
Morten Wennevik [C# MVP]
 
Thanks very much for all your help everyone. It seems there are some
great answers which I can have a bash at.

Thanks again

Dave
 
Back
Top