Sample code to illustrate:
int res1 = "a".CompareTo("A"); // res1 = -1
int res2 = "ab".CompareTo("A"); // res2 = 1
I'm seeing res1 = -1, and res2 = 1 at the end, which was a bit unexpected.
I thought res1 would return 1, since on an ASCII chart "A" (0x41) comes before "a" (0x61).
Also, it seems strange that for res2, the length of the string seems to make a difference. i.e. if "a" comes before "A" (as res1 = -1 indicates), then I would have thought that "a"withAnythingAfterIt would also come before "A"withAnythingAfterIt.
Can someone shed some light?
Thanks.
This is the expected behavior. String.CompareTo(string) does a culture sensitive comparison, using its sort order. In fact it calls CultureInfo to do the job as we can see in the source code:
public int CompareTo(String strB) {
if (strB==null) {
return 1;
}
return CultureInfo.CurrentCulture.CompareInfo.Compare(this, strB, 0);
}
Your current culture puts 'A' after 'a' in the sort order, since it would be a tie, but not after 'ab' since clearly 'ab' comes after either 'a' or 'A' in most sort orders I know. It's just the tie breaking mechanism doing its work: when the sort order would be the same, use the ordinal value!
From MSDN
Definition
Compares this instance with a specified Object and indicates whether
this instance precedes, follows, or appears in the same position in
the sort order as the specified Object.
Note
The CompareTo method was designed primarily for use in sorting or
alphabetizing operations. It should not be used when the primary
purpose of the method call is to determine whether two strings are
equivalent. To determine whether two strings are equivalent, call the
Equals method.
CompareTo is an instance method.
If the first string is bigger, the result is 1. If the first string is smaller, the result is -1. If both strings are equal, the result is 0. The number essentially indicates how much "larger" the first string is.
Console.WriteLine("a".CompareTo("A")); // -1
Console.WriteLine("ab".CompareTo("A")); // 1
Console.WriteLine("a".CompareTo("a")); // 0
Console.WriteLine("ab".CompareTo("AB")); // -1
Console.WriteLine("A".CompareTo("a")); // 1
Console.WriteLine("AB".CompareTo("ab")); // 1
Console.WriteLine("A".CompareTo("A")); // 0
Related
I'm trying to understand CompareTo() in C# and the following example made me more confused than ever. Can someone help me understand why the result for the 3rd variation is 1? 2nd word in the sentence "Hello wordd" is not the same as str1 "Hello world" so why am I getting 1? Shouldn't I get -1?
static void Main(string[] args)
{
string str1 = "Hello world";
Console.WriteLine(str1.CompareTo("Hello World"));
Console.WriteLine(str1.CompareTo("Hello world"));
Console.WriteLine(str1.CompareTo("Hello wordd"));
}
Results: -1, 0, 1
If the strings match, then CompareTo() gives 0. If they don't match, it gives a positive or negative number depending on which string comes first alphabetically.
In your example, both the results 1 and -1 indicate the strings do not match, while 0 indicates the strings match.
It looks like you are using it to determine equality and not to sort. If this is the case, then you should use Equals() instead.
The String.CompareTo method compares this instance with a specified object or String and returns an integer that indicates whether this instance precedes.
if the return value is Less than zero: This instance precedes value.
if the return value is Zero: This instance has the same position in the sort order as value.
if the return value is Greater than zero: This instance follows value.
-or-
value is null.
using System;
public class Program
{
public static void Main()
{
string t1, t2;
t1 = "Test";
t2 = "test";
Console.WriteLine(t1.CompareTo(t2)); //prints 1, expected was -1
}
}
So, it says that CompareTo() is supposed to return 1 - if it's greater than, -1 if it's less than or 0 if it's equal to the other string. In this example I am comparing "Test" with "test". As I understood, in ASCII, 'A' < 'a'.
So why does it say that t1 is greater if the only difference is first letter, and by ASCII, it should be smaller. Thanks.
The default C# string comparison (as in, if you don't specify it yourself anywhere) is a culture-aware comparison, and the rules depend on your computer's culture.
If you want to use ordinal comparison (ie ASCII as you call it, though C# strings are Unicode), you can use this instead:
Console.WriteLine(string.Compare(t1, t2, StringComparison.Ordinal));
Also note that the specification requires a negative, zero or positive result, not specifically -1. The command above will return -32 for example.
So, I'm doing an assignment for my C# class and I have a list of objects. These objects have an 'int rand' field which is assigned a random number. I then wanted to re-sort the objects in the list based on this rand field.
I found this article:
http://www.developerfusion.com/code/5513/sorting-and-searching-using-c-lists/
And it helped. I modified this line to fit my code:
people.Sort(delegate(Person p1, Person p2) { return p1.age.CompareTo(p2.age); });
And it does what I want.
What I want to know is: how does it work? That looks very confusing to me.
In fact Sort Method should sort base on some comparison, in your current code you passed comparison as delegate, you can also embed it in class definition to reduce code complexity, In fact it just needed to implement IComparable for your Person class:
public class Person : IComparable
{
public int age { get; set; }
public int CompareTo(object obj)
{
var person = obj as Person;
if (person != null)
{
if (age > person.age)
return 1;
else if (age == person.age)
return 0;
return -1;
}
return 1;
}
}
then simply use sort without delegates.
If you use Lambda notation with it gets a little easier to read IMO:
people.Sort((p1, p2) => p1.age.CompareTo(p2.age));
When you use the sort method you are sorting the list, but the method needs to know what to sort by, and this is where the delegation becomes handy as you can use the sort method to specify any sorting you want. You are looking at person 1 and person 2 and are ordering by age. If you wanted to sort by something else like a Name (If you had a Name property), you would write it as:
people.Sort((p1, p2) => string.Compare(p1.Name, p2.Name));
the list will use the function passed in (the return p1.age.CompareTo(p2.age); part) to compare the different objects in the list. It basically allows you to "teach" the list how you want the items compared.
The list will call your function, passing in 2 instances of the class that should be compared. you return -1 to say the 1st is less than the 2nd, 0 to say they are equal, and 1 to say the 2nd is greater. Your example just passes the call on to the built in comparison (that returns the same -1, 0, 1 pattern) for whatever type the age variable is, most likely an integer.
In order to sort, you need to be able to figure out if one item goes before or after another thing. It makes sense that this "comparator" would be a function or method as it compartmentalizes the knowledge away.
If you look at the documentation for CompareTo you'll notice that it's intended to return -1 (B goes before A), 0, (A and B are equal) or 1 (B goes after A).
The delegate keyword in this instance creates an anonymous function which is used as the comparator, and the body of that function calls CompareTo to compare the age property of the two people involved and return the result.
The result of calling this method on every potential pair of items (or some subset - I'm not sure exactly how Sort is implemented) is then used internally by the Sort method to figure out where to place the resulting item (in front of or behind of p2) in this example.
List.Sort uses Quick sort algo to sort the list. The worst case complexity is O(n ^ 2). Which means, in worst case, the delegate you provided will be called n ^ 2 times, where n is the number of items in list. Delegate is like pointer to function.
It passes the two objects it wishes to compare, and the delegate you provided will return with -1, 0, or 1. If its -1 it means that p1 is lesser then p2, if 0 it means both objects are same, if 1 it means p1 is greater then p2. So, in the end, the delegate you provide and the values it returns will decide whether the list contains objects in descending or ascending order.
Lets divide the problem so that you can understand each piece separately:
The Sort method:
This one takes a delegate, that contains the "how to" compare two elements of the list.
As soon as you teach the list how to compare to elements, then it can sort all elements, by comparing them in pairs.
The inline delegate
A inline delegate is the declaration of method, that does something.
delegate(Person p1, Person p2) { return p1.age.CompareTo(p2.age);
This delegate is telling how to compare two Person objects. You are telling this to the compiler: to compare p1 with p2, you should compare p1.age with p2.age.
Joining things
The following line of code contains both elements, the sort method, and the "how to" compare two People objects.
people.Sort(delegate(Person p1, Person p2) { return p1.age.CompareTo(p2.age); });
So now it knows how to sort the list.
this is related to comparing values in C#.
basically by default, in C# till date i only used forward comparison as follows:
string value1 = "";
if (value1 == null)
{
Console.WriteLine("True");
}
else
{
Console.WriteLine("False");
}
somewhere on Internet, i came across a link where it is said that while comparing values in C#, we should prefer Reverse Comparison as :
string value1 = "";
if (null == value1)
{
Console.WriteLine("True");
}
else
{
Console.WriteLine("False");
}
Now the problem is that is there any basic difference between the two ways of comparing values or not (i.e. both are same).
Looking for favorable replies.
Thanks
This is a hangover from languages where values are implicitly converted to a boolean type.
For the most part, anything that was non-null or non-0 would be treated as true, otherwise it was equivalent to false. This meant that any assignation of a constant would always be true.
This thing to remember here is that the assignment function returns the value assigned, much in the same way that 5 is the result of 2+3, myInt = 5 also returns 5.
Because of this, it was recommended that you check the variable against the constant. The null example is actually probably a bit contrived in this case, as it would, in most languages, return as false.
As an example:
if(i = 1)
{
printf("Always out\n");
}
else
{
printf("Never prints\n");
}
i is assigned the value of 1, which then returns the value of the assignment, 1, to the if statement. This is equivalent to true, and the if condition matches.
If you were to attempt to assign i to the constant 1, you wouldn't compile, and so it was recommended that you do things it that order (e.g. 1 == i).
This is not neccesary in C#, and further to your example, the recommended practice is to call if(String.IsNullOrEmpty(myStringValue)) { //.... }, as a string has... two ... default values from a semantic perspective.
Reverse comparison protects you from errors, when you use == (compare) instead of = (assign). But complier also warns you if you do this.
Why is it that the following code won't work:
endDate.AddDays(7-endDate.DayOfWeek);
While this will:
endDate.AddDays(0-endDate.DayOfWeek + 7);
?
(By "won't work" I mean results in the following compilation error: "cannot convert from 'System.DayOfWeek' to 'double'")
To expand upon what Lasse said (or rather, make it a little more explicit).
Because 0 is convertable to an Enum type,
0 - endDate.DayOfWeek becomes
(DayOfWeek)0 - endDate.DayOfWeek
And since you can subtract one enum from another and get an integer difference:
(DayOfWeek)0 - endDate.DayOfWeek == (int)endDate.DayOfWeek
Thus, since the result of the subtraction is an int, you can then add 7 to it.
endDate.AddDays(0-endDate.DayOfWeek + 7);
So, if Monday's Enum value is 1
0 - endDate.DayOfWeek == -1 + 7 == 6
However, you can't do the reverse.
endDate.DayOfWeek - 0 + 7,
because the result type of the calculation is dependant upon the leftmost side. Thus, while 0 - endDate.DayOfWeek results in an integer, endDate.DayOfWeek - 0 results in an enum DayOfWeek.
Most interestingly, you could use this side-effect to get the value of an enum without casting, though I would consider this hackish and confusing... thus to be avoided.
int enumValue = -(0 - endDate.DayOfWeek);
This is very interesting. The right way to do this is:
endDate.AddDays(7 - (int)endDate.DayOfWeek);
But, your question isn't about a solution, but a reason for the behavior. It has something to do with the way the compiler treats a zero. Either line fails if no zero is present, while both lines work if a zero is present.
You can subtract two enum values to get their integer value difference:
using System;
namespace ConsoleApplication10
{
public enum X { A, B, C, D }
public class Program
{
static void Main()
{
var x = X.D + X.A;
Console.Out.WriteLine(x);
Console.In.ReadLine();
}
}
}
Will print out 3.
But you can't add, probably makes no sense.
In the case of "0", 0 is auto-convertible to all enum types, so basically "0 - enumvalue" means the same as "(enumtype)0 - enumvalue", which again works.