C# how to linq.select - c#

i want to do the following i have variables stored in an int array called Straight i want to use Linq and get all the values when divided by 4 return 0 i tried this but it will only give me some bool variables and I'm not sure why
var a = Straight.Select(o => o % 4==0).ToArray();
any help is appreciated also i want to note that I'm still learning c# and Linq is something completely new to me
also i want to be able to check the length of the variable

The part you're looking for is Where and not Select.
var a = Straight.Where(o => (o % 4) == 0).ToArray();
Select projects your list into a new returns type which in the case of the expression (o%4) == 0 is boolean.
Where returns you the same object that fulfill the desired expression.

You need Where, not Select
var a = Straight.Where(o => o % 4 == 0).ToArray();
Select creates a projection. In your example, it turns each element of Straight into a bool.

Related

Indexing IQueryable<int>?

How do you index an IQueryable?
I am using a LINQ to sql query to get in values from a particular column. The query is as follows,
var intitalQuery = (from a in sql.GetTable<Staff_Time_TBL>()
where a.Info_Data == SelectedOption
select a.Staff_No).Distinct();
From there I want to be able index the intitalQuery variable and get values as needed.
That value is then used in another query.
My first try was this,
Column1.DataContext = sql.Staff_Time_TBLs.Where(item =>
item.Section_Data == SelectedOption &&
item.Staff_No == intitalQuery[0];
Then I tried this from here with no luck.
Column1.DataContext = sql.Staff_Time_TBLs.Where(item =>
item.Section_Data == SelectedOption &&
item.Staff_No == intitalQuery.First());
From what I can from the link is that that way gets just the first value, I want to be able to get all values via indexing. How do you go about that?
IQueryable<T> inherits from IEnumerable and as such has a wealth of extension methods to accomplish almost anything you'd need from a sequence. In particular, .ToList() turns an enumerable into a List<T> that allows efficient indexing.
.ToList() is slightly more efficient than the more obvious .ToArray() when working with sequences of unknown initial length, because .ToArray() requires an additional copy to end up with an array of exactly the right size. (But arrays are faster to loop over, so it all depends on what you're doing.)
You can do this:
public static List<Staff_Time_TBLs> GetIndexed(string staffNo){
var stuff = sql.Staff_Time_TBLs.Where(item =>
item.Section_Data == SelectedOption &&
item.Staff_No == staffNo;
return stuff.ToList();
}
//to use it...
initialQuery.ForEach(p=>{
var indexvalue = GetIndexed(p)
});

c# filter search, multiple searchboxes

List<LICENSE> licenseList = context.LICENSE.Where(l => ( string.IsNullOrEmpty(licenseID) || l.LICENSE_ID.Contains(licenseID) ) && ( string.IsNullOrEmpty(hardwareID) || l.HARDWARE_ID.Contains(hardwareID) ) ).Take(10).ToList();
This is my current solution for handling more than one searchbox. Its a search function that combines 2 or more textfields in to a search. So my questions are: Is this an ok way to filter out the passed searchstrings. And how do i use it when the queries are decimals instead of strings? Thanks
Your example is perfectly fine.
With regards to it being decimals:
If it is a nullable type, then you first have to check if it has a value, and if it has that it is not the default value for a decimal which is 0.
If it is not a nullable type, then all you have to do is check that it is/is not == to 0 which is the default type. I always just check to make sure it's greater than zero, based on the assumption that a license won't be negative.
I am going to assume that it's not a nullable type as it seems to be an inline declared var, so here is a formatted example for decimal:
List<LICENSE> licenseList =
context.LICENSE.Where(l => licenseID == 0 || l.LICENSE_ID.Contains(licenseID))
.Where(l => hardwareID == 0 || l.HARDWARE_ID.Contains(hardwareID))
.Take(10)
.ToList();
Interesting thing to note, if you don't know the default type of a field, you can always do
licenseID == default(decimal)
You may try using a foreach loop on the search boxes, modifying linq, for any of those.
object[] a = {"seach", 5}; // "Data"
string[] Search = { "asdf", "asdf" }; //Search boxes
var s = a.Where(l => ((string)l).Contains(Search[0])); //first search
for (int i = 1; i < Search.Length; i++) //consecutive searches
s = s.Where(l => ((string)l).Contains(Search[i]));
Yes it looks ok. You could also use a loop somehow like this:
var query = context.LICENSE;
foreach(var item in stringVariables) {
query = query.Where(x => string.IsNullOrEmpty(item) || l.LICENSE_ID.Contains(item));
}
and stringVariables can be predefined or some algorithm to decide whether it's a search field or not.
Concerning the numbers (and assuming your column has type int, if it is a string you don't have to change anything), you probably have a nullable number, depending on your search form. So, you also have to check whether it's null or not and whether it's the right number. You may want to cast it to a string to also have the Contains function. But that all depends on your application.

Linq Union: How to add a literal value to the query?

I need to add a literal value to a query. My attempt
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
In the above example, I get an error:
"Local sequence cannot be used in LINQ to SQL implementation
of query operators except the Contains() operator."
If I am using Entity Framework 4 for example, what could I add to the Union statement to always include the "seed" ID?
I am trying to produce SQL code like the following:
select distinct ID
from product
union
select 0 as ID
So later I can join the list to itself so I can find all values where the next highest value is not present (finding the lowest available ID in the set).
Edit: Original Linq Query to find lowest available ID
var skuQuery = Context.Products
.Where(p => p.sku > skuSeedStart &&
p.sku < skuSeedEnd)
.Select(p => p.sku).Distinct();
var lowestSkuAvailableList =
(from p1 in skuQuery
from p2 in skuQuery.Where(a => a == p1 + 1).DefaultIfEmpty()
where p2 == 0 // zero is default for long where it would be null
select p1).ToList();
var Answer = (lowestSkuAvailableList.Count == 0
? skuSeedStart :
lowestSkuAvailableList.Min()) + 1;
This code creates two SKU sets offset by one, then selects the SKU where the next highest doesn't exist. Afterward, it selects the minimum of that (lowest SKU where next highest is available).
For this to work, the seed must be in the set joined together.
Your problem is that your query is being turned entirely into a LINQ-to-SQL query, when what you need is a LINQ-to-SQL query with local manipulation on top of it.
The solution is to tell the compiler that you want to use LINQ-to-Objects after processing the query (in other words, change the extension method resolution to look at IEnumerable<T>, not IQueryable<T>). The easiest way to do this is to tack AsEnumerable() onto the end of your query, like so:
var aa = new List<long>();
aa.Add(0);
var a = Products.Select(p => p.sku).Distinct().AsEnumerable().Union(aa);
a.ToList().Dump(); // LinqPad's way of showing the values
Up front: not answering exactly the question you asked, but solving your problem in a different way.
How about this:
var a = Products.Select(p => p.sku).Distinct().ToList();
a.Add(0);
a.Dump(); // LinqPad's way of showing the values
You should create database table for storing constant values and pass query from this table to Union operator.
For example, let's imagine table "Defaults" with fields "Name" and "Value" with only one record ("SKU", 0).
Then you can rewrite your expression like this:
var zero = context.Defaults.Where(_=>_.Name == "SKU").Select(_=>_.Value);
var result = context.Products.Select(p => p.sku).Distinct().Union(zero).ToList();

How to do Linq aggregates when there might be an empty set?

I have a Linq collection of Things, where Thing has an Amount (decimal) property.
I'm trying to do an aggregate on this for a certain subset of Things:
var total = myThings.Sum(t => t.Amount);
and that works nicely. But then I added a condition that left me with no Things in the result:
var total = myThings.Where(t => t.OtherProperty == 123).Sum(t => t.Amount);
And instead of getting total = 0 or null, I get an error:
System.InvalidOperationException: The null value cannot be assigned to
a member with type System.Decimal which is a non-nullable value type.
That is really nasty, because I didn't expect that behavior. I would have expected total to be zero, maybe null - but certainly not to throw an exception!
What am I doing wrong? What's the workaround/fix?
EDIT - example
Thanks to all for your comments. Here's some code, copied and pasted (not simplified). It's LinqToSql (perhaps that's why you couldn't reproduce my problem):
var claims = Claim.Where(cl => cl.ID < 0);
var count = claims.Count(); // count=0
var sum = claims.Sum(cl => cl.ClaimedAmount); // throws exception
I can reproduce your problem with the following LINQPad query against Northwind:
Employees.Where(e => e.EmployeeID == -999).Sum(e => e.EmployeeID)
There are two issues here:
Sum() is overloaded
LINQ to SQL follows SQL semantics, not C# semantics.
In SQL, SUM(no rows) returns null, not zero. However, the type inference for your query gives you decimal as the type parameter, instead of decimal?. The fix is to help type inference select the correct type, i.e.:
Employees.Where(e => e.EmployeeID == -999).Sum(e => (int?)e.EmployeeID)
Now the correct Sum() overload will be used.
To get a non-nullable result, you need to cast the amount to a nullable type, and then handle the case of Sum returning null.
decimal total = myThings.Sum(t => (decimal?)t.Amount) ?? 0;
There's another question devoted to the (ir)rationale.
it throws an exception because the result of the combined sql query is null and this cant be assigned to the decimal var. If you did the following then your variable would be null (I assume ClaimedAmount is decimal):
var claims = Claim.Where(cl => cl.ID < 0);
var count = claims.Count(); // count=0
var sum = claims.Sum(cl => cl.ClaimedAmount as decimal?);
then you should get the functionality you desire.
You could also do ToList() at the point of the where statement and then the sum would return 0 but that would fall foul of what has been said elsewhere about LINQ aggregates.
If t has a property like a 'HasValue', then I would change the expression to:
var total =
myThings.Where(t => (t.HasValue) && (t.OtherProperty == 123)).Sum(t => t.Amount);

Change value in List<T> using Linq

I have a list where i whant to change the value of a double property in that list if that property has decimals.
If x.Value has decimals, I want to change that value to take just the first decimal woithout rounding it.
I'm trying this but can't get it right:
(Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement)
var newList =
correctionQoutas.ToList()
.ForEach(x => x.Value%1 != 0 ? x.Value = Convert.ToDouble(string.Format("{0:0.0}", x)) : x.Value = x.Value);
EDIT:
correctionQoutas is a custom object that has four properties. Double StartTime, Double EndTime, Double Value and string Id.
You can't modify a collection while you're iterating it.
Here's a simple approach
var list=correctionQoutas.ToList();
for(int i=0; i<list.Count(); i++)
{
if(list[i].Value % 1 != 0)
{
list[i].Value = Convert.ToDouble(string.Format("{0:0.0}", list[i].Value)) ;
}
}
If you need to transform the elements, then you need to use the Select() LinQ function:
var newList =
correctionQoutas.Select(x => x.Value%1 != 0 ? x.Value = Convert.ToDouble(string.Format("{0:0.0}", x)) : x.Value = x.Value);
This gives you an IEnumerable, to freeze the contents and actually do the evaluation, you can use the ToList() function after the Select() function.
If you simply need to get the elements that are of a certain type, you can do this:
var subList = correctionQoutas.OfType<MyQuota>();
That would give the subset that are MyQuota or are directly assignable to that type. It's an easy way to filter out nulls.
Since you want to change the properties of the elements in your enumeration, you should not be using linq or List<T>.ForEach for that. (Your question does not sound like you want to copy the original objects). So simply iterate over that enumeration and set the new values:
foreach (var quota in correctionQoutas)
quota.Value = (int)(quota.Value * 10) / 10d;
Note that I cut the trailing decimals by simple arithmetic instead of converting the values to strings and back.
Since you save your result to a new List anyway, you can just Select the result instead:
Oh and % wont do you any good here anyway, your number would be converted to int first, if it works at all.
The check if you have decimal places is unneccessary too, since the output will be a double anyway.
// depending on what correctionQuotas is, you might be able to omit ToList()
var newList = correctionQoutas.Select(x => Math.Truncate(10 * x.Value) / 10).ToList();
Without digging into what your actual conversion function is doing. To use a linq statement to get a new List of CorrectionQuota you would do something like this:
var newList = correctionQoutas
.Select(x => Convert(x))
.ToList();
private CorrectionQouta Convert(CorrectionQouta x){
x.Value = x.Value % 1 != 0 ?
Convert.ToDouble(string.Format("{0:0.0}", x)) : x.Value);
return x;
}
Transform the results into what you want first and then make the new List.

Categories