I have a list of objects that are returned from a web api request. Each object has a Field named "PlaceNo" which can have a value of 0 to n , or -1 if a value have not been assigned.
I need to order the list based of PlaceNo, where it need to be 0 to n , followed by all the -1.
allreports.OrderBy(x => Convert.ToInt32(x.PlaceNo)).ToList();
The current code gives all the -1 at first, then followed by 0 to n.
Note: I am doing the type conversion on PlaceNo, becuase the number is passed as string rather than an integer.
The correct way to solve this problem is to do an order-by-then-by query. Though the suggestion of the other answers -- to convert negative numbers to the maximum int -- will likely work, the best practice is to write the code so that it actually represents the operations you are trying to capture in the code. In this case you have two sort criteria, so you should have two sorting criteria in the query!
What you want to do is first do an order-by on "is the integer -1?" That is, order on a Boolean quantity. The default ordering is that false comes before true, so if you want the -1's to be sorted last, check to see if the integer is equal to -1.
That should be your order-by clause. The then-by clause is then your normal ordering -- by the integer value.
var query = from report in allreports
let place = Convert.ToInt32(report.PlaceNo)
orderby place == -1, place
select report;
var list = query.ToList();
Make sense?
allreports.OrderBy(x => {
var placeNo = Convert.ToInt32(x.PlaceNo);
return placeNo == -1 ? Int32.MaxValue : placeNo;
}).ToList();
This code is untested i just wrote this off the top of my head so feel free to correct any compilation errors.
Try the following:
allreports.OrderBy(x => Convert.ToInt32(x.PlaceNo) < 0 ? Int32.MaxValue : Convert.ToInt32(x.PlaceNo));
Related
I need to count the rows of a column except the duplicate ones
House Number
123
124
11
12
11
11
Total House Number: 4
I have searched and can't find the right syntax for my code.
I tried dictionary but it seems that it is not right for my code.
I am a complete beginner in c#
//Total House
int House = 0;
for (int row = 0; row < dataGridView1.Rows.Count; ++row)
{
if ((string)dataGridView1.Rows.[row].Cells("House_Number").Distinct())
{
House++;
}
}
TotalHouse.Text = "Total Houses " + $"{House}";
I tried the above code but it has an error Identifier expected.
Your code has a few potential problems, but let's start with the ones that will prevent it from compiling.
if ((string)dataGridView1.Rows.[row].Cells("House_Number").Distinct())
One problematic bit here is Rows.[row]. There shouldn't be a period there. If you have a period like that, C# will expect an identifier to follow it, not another operator. In this case, you have the [] operator following it, which is invalid. It should probably look like this:
if ((string)dataGridView1.Rows[row].Cells("House_Number").Distinct())
We're getting closer. However, the test inside an if statement must evaluate to a bool--that's true or false. Yours evaluates to a string because you're casting the whole thing to a string. That's because this part runs first:
dataGridView1.Rows[row].Cells("House_Number").Distinct()
Then this part runs:
(string)
So the whole thing becomes a string. We'll have to remove that (string) bit.
Let's take a closer look at dataGridView1.Rows[row].Cells("House_Number").Distinct(). Cells isn't a method--it's a property. That means you can't use the syntax Cells("House_Number"). However, the result of Cells is a DataGridViewCellCollection, which allows [] syntax, so you can do something like Cells["House_Number"].
Distinct() isn't going to give you a bool value--it will give you a collection of unique cells in the form of something called an IEnumerable.
dataGridView1.Rows[row].Cells.Distinct() isn't going to give you distinct cells in a column--it's going to give you distinct cells in a row. That's probably not what you want.
You're probably going to want something that looks like this:
int houses = dataGridView1.Rows
.Cast<DataGridViewRow>()
.Select(r => (int)r.Cells["House_Number"].Value)
.Distinct()
.Count();
Walking through this:
Start with dataGridView1.
Get a DataGridViewCellCollection of rows: .Rows
DataGridViewCellCollection is pretty old, so it implements IEnumerable instead of IEnumerable<DataGridViewRow>. We need to turn it into an IEnumerable<DataGridViewRow>, so we call LINQ's Cast<DataGridViewRow>().
Use LINQ to turn that into an IEnumerable<int>: .Select(r => (int)r.Cells["House_Number"].Value)
a. The argument to Select is a lambda expression. It takes one argument, r, which is a DataGridViewRow. It will return an int.
b. Get the cells for the row: .Cells
c. Get the specific cell we want: ["House_Number"]
d. Get the value of that cell
e. The value is returned as an object; we need to cast it to an int: (int)
Use LINQ to turn that IEnumerable<int> into another one that only has distinct values: .Distinct()
Count our results: .Count()
You'll need a reference to System.Linq for this to work. Put this at the top of your file if it isn't already there:
using System.Linq;
You can achieve this easily by adding a nuget reference to System.Data.DataSetExtensions
and then using linq to select the distinct house numbers:
var count = dataGridView1.AsEnumerable().Select(dr => dr["House_Number"]).Distinct().Count();
Otherwise you could achieve this using a hashset:
var hs = new HashSet<string>();
foreach (DataRow dataRow in dataGridView1.Rows)
{
hs.Add(dataRow["House_Number"].ToString());
}
TotalHouse.Text = "Total Houses " + $"{hs.Count}";
I am relatively new to programming. I have an array of objects which isn't necessarily full (may include null rows). And I want to sort it by one of the class parameters "int moveScore".
This is my array (currently holds only 32 entries)
Score[] firstPlyScore = new Score[1000];
I tried 2 things for sorting
1
In the "Score" class, i inherited "IComparable" and used the "CompareTo" method as follows
public int CompareTo(object obj)
{
Score x = (Score)obj;
if (this.moveScore < x.moveScore)
return -1;
if (this.moveScore > x.moveScore)
return 1;
return 0;
}
I called it using;
Array.Sort(firstPlyScore);
The problem is that it does sort correctly but at the end of the array. Meaning rows 0-966 are "null" and 967-999 are sorted correctly (967 with highest "int", 999 with lowest).
Is there any way to fix this.
2
I also tried this
Array.Sort(firstPlyScore, delegate
(Score x, Score y) { return x.moveScore.CompareTo(y.moveScore); });
Here the problem was that it crashed when it reached a "null" row.
Help most appreciated!
The default comparison behavior is for null values to be ordered before non-null values. If you want to override this behavior, a custom Comparison<Score> like in your second example would be the way to go.
delegate (Score x, Score y) {
if (x == null)
return y == null ? 0 : 1;
if (y == null)
return -1;
return x.moveScore.CompareTo(y.moveScore);
}
This will keep the null items at the end of the array.
To sort in descending order, just swap the x and y references in the last line.
firstPlyScore = firstPlyScore
.Where(x => x != null)
.OrderByDescending(x => x.moveScore)
.ToArray();
You can use Linq to Entities to sort and then convert back to an array it will re-size your array to the correct length needed without null issue
var list = firstPlyScore.OrderByDescending(x => x.MoveScore).ToList();
//here how you can get always 1000 array length as you asked
for (int i = list.Count-1; i < 1000; i++)
{
list.Add(null);
}
firstPlyScore = list.ToArray();
}
In the beginning of your compare method
if(obj == null) return 0;
The problem is that it does sort correctly but at the end of the
array. Meaning rows 0-966 are "null" and 967-999 are sorted correctly
(967 with highest "int", 999 with lowest). Is there any way to fix
this.
If you need something, whose size can change, you are looking for a List<int>, instead of using arrays.
Arrays should be used for Lists that do not change (e.g. a chess board).
The List also provides you a method called Sort.
As mentioned by others, you can also use LINQ to achieve what you seek for.
Do you really need to keep 1000 items in the list even if most of them are null ?
I would suggest to check the logic behind it to see if you can prevent that because that's taking a lot of space for nothing.
Otherwise, 2 possibilities:
Check for Obj == null in your compare function. But it might still fail if the comparing item is null
Create a custom class for your score and make Icomparable (Check this link for several example about how to sort arrays
I have a sorted array of strings.
Given a string that identifies a prefix, I perform two binary searches to find the first and last positions in the array that contain words that start with that prefix:
string [] words = {"aaa","abc","abcd","acd"};
string prefix = "abc";
int firstPosition = Array.BinarySearch<string>(words, prefix);
int lastPosition = Array.BinarySearch<string>(words, prefix + char.MaxValue);
if (firstPosition < 0)
firstPosition = ~firstPosition;
if (lastPosition < 0)
lastPosition = ~lastPosition;
Running this code I get firstPosition and lastPosition both equal to 1, while the right answer is to have lastPosition equal to 3 (i.e., pointing to the first non-matching word).
The BinarySearch method uses the CompareTo method to compare the objects and I have found that
("abc"+char.MaxValue).CompareTo("abc")==0
meaning that the two string are considered equal!
If I change the code with
int lastPosition = Array.BinarySearch<string>(words, prefix + "z");
I get the right answer.
Moreover I have found that
("abc"+char.MaxValue)==("abc")
correctly (with respect to my needs) returns false.
Could you please help me explaining the behavior of the CompareTo method?
I would like to have the CompareTo method to behave like the ==, so that the BinarySearch method returns 3 for lastPosition.
string.CompareTo() does a current-culture compare. Internally it uses StringComparer.CurrentCulture, whereas the string equals-operator does a culture-invariant compare.
For example, if the current-culture is "DE", you will get the same results with "ss" and "ß":
Console.WriteLine("ss".CompareTo("ß")); // => 0
Console.WriteLine("ss" == "ß"); // => false
What you want is a culture-invariant compare, which you will get by using StringComparer.Ordinal:
StringComparer.Ordinal.Compare("ss", "ß"); // => -108
StringComparer.Ordinal.Compare("abc"+char.MaxValue, "abc"); // => 65535
According to the MSDN, string.CompareTo should not be used to check whether two strings are equal:
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.
To get the behavior you wish, you could make use of the overload that accepts an IComparer<T>:
int lastPosition = Array.BinarySearch<string>(words, prefix + char.MaxValue,
StringComparer.Ordinal);
This will return -4 for lastPosition as there is no string with that prefix in the array. I don't understand why you expect 3 in that case...
I've only been playing with linq to sql and lambda expressions for the first time for a few days and I want to do the following.
I've got a string extension method that returns a double. The extension method tests two strings and returns a similarity score.
I have a list of string values from a column in a table using linq to sql and I want to use the extension method as a way of filtering out the only those strings whose similarity score is equal to or greater than the input string.
I've got the below so far. I don't seem to be able to test the value of the returned double.
List<int> ids = dc.ErrorIndexTolerances
.Where(n => n.Token.Distance(s) => .85)
.Select(n => n.ID)
.ToList();
The Distance Method is the extension method that returns a double. Both Token and s are string. ID is an integer ID field within a table.
Does anyone have any tips?
The greater or equal operator is >=, not =>.
List<int> ids =
dc.ErrorIndexTolerances.Where(n => n.Token.Distance(s) >= .85)
.Select(n => n.ID).ToList();
Perhaps this should be
n.Token.Distance(s) >= .85)
Just a typo :-)
Does anyone have any tips?
I have a tip... never use "greater than", only use "less than".
.Where(n => .85 <= n.Token.Distance(s))
I follow this rule mainly because of date logic. When comparing 5 sets of dates, it's good to never make the mistake of mis-reading the sign. The small one is on the left and the big one is on the right, 100% of the time.
.Where(acct => acct.CreateTime <= now
&& acct.StartTime <= order.OrderDate
&& order.FulfilledDate <= acct.EndTime)
If you have a string of "1,2,3,1,5,7" you can put this in an array or hash table or whatever is deemed best.
How do you determine that all value are the same? In the above example it would fail but if you had "1,1,1" that would be true.
This can be done nicely using lambda expressions.
For an array, named arr:
var allSame = Array.TrueForAll(arr, x => x == arr[0]);
For an list (List<T>), named lst:
var allSame = lst.TrueForAll(x => x == lst[0]);
And for an iterable (IEnumerable<T>), named col:
var first = col.First();
var allSame = col.All(x => x == first);
Note that these methods don't handle empty arrays/lists/iterables however. Such support would be trivial to add however.
Iterate through each value, store the first value in a variable and compare the rest of the array to that variable. The instant one fails, you know all the values are not the same.
How about something like...
string numArray = "1,1,1,1,1";
return numArrray.Split( ',' ).Distinct().Count() <= 1;
I think using List<T>.TrueForAll would be a slick approach.
http://msdn.microsoft.com/en-us/library/kdxe4x4w.aspx
Not as efficient as a simple loop (as it always processes all items even if the result could be determined sooner), but:
if (new HashSet<string>(numbers.Split(',')).Count == 1) ...