How to use Linq to extract double value and join to array? - linq

Below is a logic to extract value from string.
I am wondering is it possible to run with Linq statement.
Any hint?
string r = "#1:234.1;#2:2344.2:#3:38.0";
List<double> v = new List<double>();
foreach (var x in r.Split(';'))
{
foreach (var y in x.Split(':'))
{
if (string.IsNullOrWhiteSpace(y) == false && y.StartsWith("#") == false)
{
v.Add(double.Parse(y));
}
}
}
Console.WriteLine(string.Join("\n", v.ToArray()));

This is the equivalent code in Linq form
var v = r.Split(';')
.SelectMany(x => x.Split(':'))
.Where(x => !string.IsNullOrWhiteSpace(x) && !x.StartsWith("#"))
.Select(double.Parse)
.ToList();

Related

LinqKit Core PredicateBuilder not functioning correctly

I have this query:
var query = LinqKit.PredicateBuilder.New<Resume>();
if (selectedWorkFieldID != 0)
{
query = query.And(js => js.WorkFieldID == selectedWorkFieldID);
if (!(selectedJobIDs.Contains(0) && selectedJobIDs.Count() == 1))
{
foreach (int jobID in selectedJobIDs)
query = query.Or(js => js.JobID == jobID);
}
}
var finalQuery = context.Resumes.AsNoTracking().Include(r => r.ResumeSkills)
.ThenInclude(rs => rs.Skill).Include(r => r.JobSeeker).ThenInclude(r => r.Profile)
.AsExpandable().Where(query);
count = finalQuery.Count();
resumes = finalQuery.Skip(args.Skip.Value).Take(args.Top.Value).ToList<Resume>();
This query returns All resumes not filtered ones. When I debug, the debugger curser enters the foreach block that filters with or, and there is one jobID in selectedJobIDs but the query returns all resumes. it seems the predicate builder not working at all. How to solve this?
I changed code to this:
if (selectedWorkFieldID != 0)
{
query = query.And(js => js.WorkFieldID == selectedWorkFieldID);
if (!(selectedJobIDs.Contains(0) && selectedJobIDs.Count() == 1))
{
var query2 = LinqKit.PredicateBuilder.New<Resume>();
foreach (int jobID in selectedJobIDs)
query2 = query2.Or(js => js.JobID == jobID);
query.And(query2);
}
}
and it is corrected.

Linq to Entities Where Or clause

Have read other responses to similar issuebut I can not use PredicateBuilder, or copy its source. I'm trying what I've read here:
<https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/
but as I'm newb, am having trouble with translating what I'm reading to what I'm applying. I have created a L2E query, and trying to append a series of OR clauses onto the WHERE:
So as simplified snippet (this one will be AND'd with the previously already defined WHERE clause):
if (firstParm == "realtor")
query = query.Where(x=> x.A == "realtor");
Now trying to OR:
if (secondParm == "clown")
// how to add this one as an OR to the above query:
query = query.OR(x=> x.fool == "clown");
I understand this can be done also with Union, but not clear on the syntax:
query = query.Union(x=> x.fool == "clown"); // ??
I've also referenced:
Combining two expressions (Expression<Func<T, bool>>)
Unable to create a compound Expression<Func<string, bool>> from a set of expressions
but again, I am new to LINQ and especially Expression Trees, so need more fillin.
There are two ways to generate expressions.
Use the compiler to do it.
Expression<Func<Person, bool>> = p => p.LastName.Contains("A");
Limitations: The only expressions that can be generated this way are instances of LambdaExpression. Also, it is rather complicated to extract parts of the expression and combine with other parts.
Use the static methods at System.Linq.Expressions.Expression.
In order to generate dynamic expressions, you can either choose between different compiler-generated expressions:
// using Record and Records as a placeholder for the actual record type and DbSet property
Expression<Func<Record,bool>> expr;
if (firstParam == "realtor") {
if (secondParam == "clown") {
expr = x => x.A == "realtor" || x.fool == "clown";
} else {
expr = x => x.A == "realtor";
}
} else {
if (secondParam == "clown") {
expr = x => x.fool="clown";
} else {
expr = x => false;
}
}
var ctx = new MyDbContext();
var qry = ctx.Records.Where(expr).Select(x => new {x.A, x.fool});
Or, you can dynamically create the expression using the static methods:
(Add using System.Linq.Expressions; and using static System.Linq.Expressions.Expression; to the top of the file.)
Expression expr;
var parameter = Parameter(typeof(Record));
if (firstParam == "realtor") {
expr = Equals(
MakeMemberAccess(parameter, typeof(Record).GetProperty("A")),
Constant("realtor")
);
}
if (secondParam == "clown") {
var exprClown = Equals(
MakeMemberAccess(parameter, typeof(Record).GetProperty("fool")),
Constant("clown")
);
if (expr == null) {
expr = exprClown;
} else {
expr = Or(expr, exprClown);
}
}
var lambda = Lambda<Func<Record,bool>>(expr, new [] {parameter});
var ctx = new MyDbContext();
var qry = ctx.Records.Where(lambda).Select(x => new {x.A, x.fool});
Given a query with a type unknown at compile time, so any variable referring to it must be IQueryable only, and not IQueryable<T>:
IQueryable qry = ctx.GetQuery(); //dynamically built query here
var parameter = Parameter(qry.ElementType);
if (firstParam == "realtor") {
expr = Equals(
MakeMemberAccess(parameter, qry.ElementType.GetProperty("A")),
Constant("realtor")
);
}
if (secondParam == "clown") {
var exprClown = Equals(
MakeMemberAccess(parameter, qry.ElementType.GetProperty("fool")),
Constant("clown")
);
if (expr == null) {
expr = exprClown;
} else {
expr = Or(expr, exprClown);
}
}
var lambda = Lambda(expr, new [] {parameter});
//Since we don't have access to the TSource type to be used by the Where method, we have
//to invoke Where using reflection.
//There are two overloads of Queryable.Where; we need the one where the generic argument
//is Expression<Func<TSource,bool>>, not Expression<Func<TSource,int,bool>>
var miWhere = typeof(Queryable).GetMethods().Single(mi => {
mi.Name == "Where" &&
mi.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2
});
qry = miWhere.Invoke(null, new [] {qry, lambda});
For Or you can try
if (secondParm == "clown")
{
query = query.Where(x=> x.fool == "clown" || x.fool==x.fool);
}
OR
if (secondParm == "clown")
{
query = query.Where(x=> x.fool == "clown" || true );
}

linq query for update more than one record

I have a table named industries. In this my fields are
workfor_id,
workfor_usr_id,
workfor_industry_id.
With the same values of workfor_id, I have different workfor_industry_id's.
foreach (var k in us){
var ind = dbContext.industries.Where(i => i.workfor_id ==
k.id).Select(i => i).FirstOrDefault();
string ind2 = k.industry;
var industryParts = ind2.Split(',');
var o = (industryParts.Length);
for (c = 0; c < o; c++){
ind.workfor_id = Convert.ToInt16(k.id);
ind.workfor_industry_id = Convert.ToInt16(k.industryid); }
}
To update workfor_industry_id field I have implemented inner loop inside the foreach loop to get the values of workfor_industry_id's.here same record is over loading with different workfor_industry_id's.
can you tell me how to implement this.
UPDATED
This update adds a little more error checking and assumes that -1 is never a valid value for industry_id
short GetShort(string value) {
short returnValue;
value = (value ?? string.Empty).Replace("\"",null);
return short.TryParse(value, out returnValue) ? returnValue : (short)-1;
}
foreach (var k in us){
var id=Convert.ToInt16(k.id);
var toRemove=from i in dbContext.industries
where i.workfor_id == k.id
select i;
var toAdd = from x in (k.industry ?? string.Empty).Split(',')
select new Industry {
workfor_id=id,
workfor_industry_id=GetShort(x)
};
dbContext.industries.DeleteAllOnSubmit(toRemove);
dbContext.industries.InsertAllOnSubmit(toAdd.Where(x=>x.workfor_industry_id != -1));
}
dbContext.SubmitChanges();

How to select decreasing sub-series with Linq

I have a list of prices ordered by date. I need to select all monotonously decreasing values. The following code works:
public static List<DataPoint> SelectDecreasingValues(List<DataPoint> dataPoints)
{
var ret = new List<DataPoint>(dataPoints.Count);
var previousPrice = dataPoints[0].Price;
for (int i = 0; i < dataPoints.Count; i++)
{
if (dataPoints[i].Price <= previousPrice)
{
ret.Add(dataPoints[i]);
previousPrice = dataPoints[i].Price;
}
}
return ret;
}
However, is there a shorter/cleaner way to accomplish it with Linq?
This code is equivalent:
previousPrice = dataPoints[0].Price;
var ret = dataPoints.Where(x => {
if(x.Price <= previousPrice)
{ previousPrice = x.Price; return true;}
return false;
}).ToList();
However, if you don't need to have a list, go with plain enumerables and drop the ToList at the end. That way you can make use of the deferred execution feature built into LINQ.
The following code is also equivalent:
DataPoint previous = dataPoints.FirstOrDefault();
var ret = dataPoints.Where(x => x.Price <= previous.Price)
.Select(x => previous = x).ToList();
This works because of the deferred execution in LINQ. For each item in dataPoints it will first execute the Where part and then the Select part and only then will it move to the second item in dataPoints.
You need to decide which version you want to use. The second one is not as intention revealing as the first one, because you need to know about the internal workings of LINQ.
public IEnumerable<T> WhereMonotonicDecreasing<T>(
IEnumerable<T> source,
Func<T, IComparable> keySelector)
{
IComparable key;
bool first = true;
foreach(T t in source)
{
if (first)
{
key = keySelector(t);
yield return t;
first = false;
}
else
{
IComparable newKey = keySelector(t);
if (newKey.CompareTo(key) < 0)
{
key = newKey;
yield return t;
}
}
}
}
Called by:
dataPoints.WhereMonotonicDecreasing(x => x.Price);
previousPrice = dataPoints[0];
dataPoints.Where(p => p.Price <= previousPrice.Price)
.Select(p => previousPrice = p);
You can then use .ToList() if you really need one.
How about (untested):
return dataPoints.Take(1)
.Concat(dataPoints.Skip(1)
.Zip(dataPoints,
(next, previous) =>
new { Next = next, Previous = previous })
.Where(a => a.Next.Price <= a.Previous.Price)
.Select(a => a.Next))
.ToList();
Essentially, this overlays a "one-deferred" version of the sequence over the sequence to produce "next, previous" tuples and then applies the relevant filters on those tuples. The Take(1) is to pick the first item of the sequence, which it appears you always want.
If you don't care for the readability of the variable names, you could shorten it to:
return dataPoints.Take(1)
.Concat(dataPoints.Skip(1)
.Zip(dataPoints, Tuple.Create)
.Where(a => a.Item1.Price <= a.Item2.Price)
.Select(a => a.Item1))
.ToList();

LINQ: Group By + Where in clause

I'm trying to implement a T-SQL equivalent of a where in (select ...) code in LINQ.
This is what I have now:
int contactID = GetContactID();
IEnumerable<string> threadList = (from s in pdc.Messages
where s.ContactID == contactID
group 1 by new { s.ThreadID } into d
select new { ThreadID = d.Key.ThreadID}).ToList<string>();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group new { s } by new { s.ThreadID } into d
let maxMsgID = d.Where(x => x.s.ContactID != contactID).Max(x => x.s.MessageID)
select new {
LastMessage = d.Where(x => x.s.MessageID == maxMsgID).SingleOrDefault().s
};
However, my code won't compile due to this error for the ToList():
cannot convert from
'System.Linq.IQueryable<AnonymousType#1>'
to
'System.Collections.Generic.IEnumerable<string>'
Anyone have any suggestions on how to implement this? Or any suggestions on how to simplify this code?
Your query returns a set of anonymous types; you cannot implicitly convert it to a List<string>.
Instead, you should select the string itself. You don't need any anonymous types.
Change it to
var threadList = pdc.Messages.Where(s => s.ContactID == contactID)
.Select(s => s.ThreadID)
.Distinct()
.ToList();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group s by s.ThreadID into d
let maxMsgID = d.Where(x => x.ContactID != contactID).Max(x => x.MessageID)
select new {
LastMessage = d.Where(x => x.MessageID == maxMsgID).SingleOrDefault()
};

Resources