LINQ, Lambda, C#, extension methods - c#

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)

Related

How to compare two lists in C#?

I have two lists to compare:
I want to compare List A and List B such that if any of the dates from ListB is present in List A then return true.
For example, if 14-01-2020 (which is in List B) is present in List A (which is definitely present) then it should return true.
How to do that?
Please note: The data in List A contains the dates of an entire month, whereas List B contains only a few dates.
If any of the dates from ListB is present in List A then return true.
return ListB.Any(x => ListA.Contains(x));
or vice versa:
return ListA.Any(x => ListB.Contains(x));
Which one is better for you will depend on the nature of your data, but I'd normally favor running Contains() over the shorter sequence.
Additionally, I see this:
The data in List A contains the dates of an entire month
Depending on exactly what you mean, you may be able to take advantange of that fact:
var start = A.Min(x => x);
var stop = A.Max(x => x);
return ListB.Any(x => x >= start && x <= stop);
Finally, if you know the data in one or both of the sequences is sorted, you can optimize these significantly.

Linq statement : Order by

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));

lambda expressions for comparing whether or not a string[] contains a given string

My project was working just fine until I had to account for an array of strings and not just one... I don't know how to fix this. The Country is a property of the current class this method is in. It used to be a single string but now is an array.
Originally it looked like this:
private Expression<Func<Payment, bool>> CountryMatches()
{
if (Country.Length < 1) return Skip;
return payment => payment.Country.ToLower().Contains(Country.ToLower());
}
What I can't figure out is how to set it up so that if ANY of the strings in Country match payment.Country... and of course this is passing back an expression... This is my best guess (but obviously not correct) of how to do what I need to do:
private Expression<Func<Payment, bool>> CountryMatches()
{
if (Country.Length < 1) return Skip;
return payment => payment.Country.ToLower() == Country.Any().ToLower();
}
You want to check all the contents of Country against payment.Country, like this:
return payment => Country.Any(
c => payment.Country.ToLower().Contains(c.ToLower()));
That said, this is a rather bad way to check if one string is a substring of another mainly because it does a lot of unnecessary work by converting to lowercase again and again. Here's a better way to do it:
return payment => Country.Any(
c => payment.Country.IndexOf(c, StringComparison.OrdinalIgnoreCase) >= 0);
.Any Takes a lambda in, which can perform your comparison. The above code assums it returns some object which equates to true when compared, but Any returns a simple bool. Thus, try
return payment =>
Country.Any(c =>
string..Equals(payment.Country, c, StringComparison.InvariantCultureIgnoreCase));
Also note that .Equals is often preferable to the "==" operator, particularly with strings, wherin you can pass the StringComparison parameter.

Non-determinstic choice with amb-operator

Is it possible to implement McCarthy's amb-operator for non-deterministic choice in C#?
Apparently .NET lacks continuation support but yield return could be useful. Would this be possible in other static .NET-languages like F#?
Yes, yield return does a form of continuation. Although for many useful cases, Linq provides functional operators that allow you to plug together a lazy sequence generator, so in fact in C# 3 it isn't necessary to use yield return so much (except when adding more Linq-style extensions of your own to plug gaps in the library, e.g. Zip, Unfold).
In the example we factorise an integer by brute force. Essentially the same example in C# can be done with the built-in Linq operators:
var factors = Enumerable.Range(2, 100)
.Join(Enumerable.Range(2, 100),
n => 1, n => 1, (i, j) => new { i, j })
.First(v => v.i*v.j == 481);
Console.WriteLine("Factors are " + factors.i + ", " + factors.j);
Here the starting points are my two calls to Enumerable.Range, which is built-in to Linq but you could implement yourself as:
IEnumerable<int> Range(int start, int stop)
{
for (int n = start; n < stop; n++)
yield return n;
}
There are two odd parameters, the n => 1, n => 1 parameters to Join. I'm picking 1 as the key value for Join to use when matching up items, hence all combinations will match and so I get to test every combination of numbers from the ranges.
Then I turn the pair of values into a kind of tuple (an anonymous type) with:
(i, j) => new { i, j })
Finally, I pick the first such tuple for which my test is satisfied:
.First(v => v.i*v.j == 481);
Update
The code inside the call to First need not be merely a short test expression. It can be a whole lot of imperative code which needs to be "restarted" if the test fails:
.First(v =>
{
Console.WriteLine("Aren't lambdas powerful things?");
return v.i*v.j == 481;
);
So the part of the program that potentially needs to be restarted with different values goes in that lambda. Whenever that lambda wants to restart itself with different values, it just returns false - the equivalent of calling amb with no arguments.
This is not an answer to your question, but it may get you what you want.
amb is used for nondeterministic computing. As you may know, Prolog is a nondeterministic language using the notion of unification to bind values to variables (basically what amb ends up doing).
There IS an implementation of this functionality in C#, called YieldProlog. As you guessed, the yield operator is an important requisite for this.
http://yieldprolog.sourceforge.net/

How to Compare Values in Array

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) ...

Categories

Resources