Linq Lambda get two properties as string from aggretation - c#

Inside a linq query to an anonymous select I want to concatenate strings from two properties.
For instance to find the full name of the oldest person in some grouping of persons.
var personsAndOldest = db.Persons.GroupBy(person => person.SomeThingThatCanBeGroupedForPerson).Select(a => new
{
FirstName = a.FirstOrDefault().FirstName,
LastName = a.FirstOrDefault().LastName,
BirthDate = a.FirstOrDefault().BirthDate,
FullnameOfOldes = a.Aggregate((pers1, pers2) => pers1.BirthDate > pers2.BirthDate ? pers1 : pers2).FirstName + " " //How do I get LastName of the old one (without using the full aggregate again)
});
Do I have to write the full aggregation again to get the LastName after the firstname and whitespace?

You could use a lambda statement in the Select:
var personsAndOldest = db.Persons.GroupBy(person => person.SomeThingThatCanBeGroupedForPerson).Select(a =>
{
var first = a.First();
var oldest = a.Aggregate((pers1, pers2) => pers1.BirthDate > pers2.BirthDate ? pers1 : pers2);
return new
{
FirstName = first.FirstName,
LastName = first.LastName,
BirthDate = first.BirthDate,
FullnameOfOldes = oldest.FirstName + " " + oldest.LastName)
};
});

You can do this as
var personsAndOldest = db.Persons
.GroupBy(person => person.SomeThingThatCanBeGroupedForPerson)
.Select(g => new
{
a = g.First(),
o = g.Aggregate((pers1, pers2) =>
pers1.BirthDate > pers2.BirthDate ? pers1 : pers2)
})
.Select(pair => new
{
FirstName = pair.a.FirstName,
LastName = pair.a.LastName,
BirthDate = pair.a.BirthDate,
FullnameOfOldes = pair.o.FirstName + " " + pair.o.LastName
});

You can use let to introduce new range variables.
You don't need to specify property name for anonymous type if it equals name of assigned property
I think OrderBy will find oldest person (but you can use aggregate and compare performance)
I believe that oldest person is one with minimal birth date, so you need to change aggregation to pers1.BirthDate < pers2.BirthDate ? pers1 : pers2
So
var personsAndOldest = from p in db.Persons
group p by p.SomeThingThatCanBeGroupedForPerson into g
let first = g.FirtOrDefault()
let oldest = g.OrderBy(x => x.BirthDate).FirstOrefault()
select
{
first.FirstName,
first.LastName,
first.BirthDate,
FullnameOfOldes = oldest.FirstName + " " + oldest.LastName
};

Related

Putting one column from a multi column query into a list

I am trying to put the Column LANS from this multi column query into a list.
And the First and Last name into a separate list.
I just can't figure out how to make it work.
CODE:
//Resource
var ResourceFilter = _context.INT_CertificationsXREF.Include(i => i.RIM_Resource)
.Where(i => i.RIM_Resource.LAN == i.RIM_Resource.LAN)
.Where(i => i.Approved == true)
.Where(i => i.RIM_Resource.LAN != UserInformation.Globals.LANID)
.Select( i => i.RIM_Resource.FirstName + i.RIM_Resource.LastName + i.RIM_Resource.LAN)
.Distinct()
.ToList();
var ResourceFilterNames = ResourceFilter.RIM_Resource.Firstname + RIM_Resource.LastName.ToList();
var ResourceFilterLANS = ResourceFilter.RIM_Resource.LAN.ToList();
Desired OutPut
ResourceFilterNames = Firstname" "Lastname, Firstname" "Lastname, ect....
ResourceFilterLANS = NQ90, RE97, I3R9, ect.
The list also need to be in sync order wise.
If I understood your question...i think what you want is:
var ResourceFilter = _context.INT_CertificationsXREF.Include(i => i.RIM_Resource)
.Where(i => i.RIM_Resource.LAN == i.RIM_Resource.LAN)
.Where(i => i.Approved == true)
.Where(i => i.RIM_Resource.LAN != UserInformation.Globals.LANID)
.Select( i => new {
fullName = i.RIM_Resource.FirstName + " " + i.RIM_Resource.LastName,
Lan = i.RIM_Resource.LAN
})
.Distinct()
.ToList();
the you can retrieve the list of "fullNames" and "LANS" like this
List<string> fullNames = ResourceFilter.Select(x => x.fullName).ToList();
List<string> LANS = ResourceFilter.Select(x => x.Lan).ToList();
hope it helps
Based on your desired output I think this solution will work for you
var ResourceFilterNames= string.Join(",", ResourceFilter.Select(x => ( x.Firstname
+ " " + x.LastName )));
var ResourceFilterLANS = string.Join(",", ResourceFilter.Select(x=> x.Lan));
In order this solution to work you should delete Select from ResourceFilter or rewrite it like below:
.Select( i => new { i.RIM_Resource.FirstName, i.RIM_Resource.LastName, i.RIM_Resource.LAN})
I will use the following class as my test input:
public class MyData
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Lan { get; set; }
}
My first solution assumes that your combination of FirstName and LastName is unique.
To hold your two lists in sync I will use a dictionary:
// var resource = new List<MyData>();
var dictionary = resource.ToDictionary(r => r.FirstName + r.LastName, r => r.Lan);
My second solution assumes that they are not unique so you can use this:
var dictionary = new Dictionary<string, List<string>>();
foreach(var res in resource)
{
var key = res.FirstName + res.LastName;
List<string> value;
if ( dictionary.TryGetValue(key, out value))
{
value.Add(res.Lan);
}
else
{
dictionary[key] = new List<string> {res.Lan};
}
}

Attempted Left Join in Query Syntax and Lambda results in "The method or operation is not implemented."

I've attempted to write this query two different ways, and no matter how I write it, I can't get the join to pan out. It just keeps tossing "The method or operation is not implemented." at me. What I'm trying to do is, in SQL, simple: Obtain a list of items from table A (below - Customer) where there is no corresponding listing in table B (below - SalesReps).
Attempted Lamda:
var customers =
_sms.CurrentSession.Query<customer>()
.GroupJoin(_sms.CurrentSession.Query<salesReps>(),
c => c.Id,
sr => sr.CustomerId,
(x, y) => new {c = x, sr = y})
.SelectMany(xy => xy.sr.DefaultIfEmpty(),
(x, y) => new {c = x.c, sr = y})
.Where(data => data.sr.SalesRepId == null)
.Select(data => new CustomerDTO
{
Id = data.c.Id,
FullName = data.c.FirstName + " " + data.c.LastName,
FirstName = data.c.FirstName,
LastName = data.c.LastName
});
var custList = customers .ToList();
Attempted Query Syntax:
var customers = from c in _sms.CurrentSession.Query<customer>()
join sr in _sms.CurrentSession.Query<salesReps>()
on c.Id equals sr.CustomerId
into joinedData
from jd in joinedData.DefaultIfEmpty()
where c.IsEmployee == false
&& c.CustomerOptions.Company.Id == companyGuid
&& jd.SalesRepId == null
select
new CustomerDTO
{
Id = c.Id,
FullName = c.FirstName + " " + c.LastName,
FirstName = c.FirstName,
LastName = c.LastName
};
var custList = customers .ToList();
I get the impression that the issue is on the check for null sales reps, but I'm not sure.

how to use entity framework to group by date not date with time

my code:
//get data
var myData = from log in db.OperationLogs
group log by log.CreateTime.Date into g
orderby g.Key
select new { CreateTime = g.Key, Count = g.Count() };
this code will throw an exception like entity framework does not support get Date operation.
because log.createtime both have date and time, i want to group by date, how should i do
Use EntityFunctions.TruncateTime Method (Nullable<DateTime>). It will be transalated into TRUNCATETIME() TSQL function in generated SQL query, which does what you need:
Returns the expression, with the time values truncated.
So your code should be as follows:
//get data
var myData = from log in db.OperationLogs
group log by EntityFunctions.TruncateTime(log.CreateTime) into g
orderby g.Key
select new { CreateTime = g.Key, Count = g.Count() };
Here is an easier way to do it for later Entity Framework versions.
var query = Data
.GroupBy(o => new { EventDate = o.EventDate.Date })
.Select(s => new SalesData()
{
EventDate = s.Key.EventDate,
Amount = s.Sum(o => o.Amount),
Qty = s.Sum(o => o.Qty),
RefundAmount = s.Sum(o => o.RefundAmount),
RefundQty = s.Sum(o => o.RefundQty),
})
.OrderBy(o => o.EventDate)
.ToList();
return query;
var result = from s in entitiesModel.TvysFuelTankDatas
orderby s.Datetime ascending
group s by new { y = s.Datetime.Year, m = s.Datetime.Month + "/", d = s.Datetime.Day + "/" } into g
select new WellDrillData { Date = Convert.ToDateTime(g.Key.d.ToString() + g.Key.m.ToString() + g.Key.y.ToString()), Depth = (double)g.Sum(x => x.Difference) };
List<WellDrillData> dailyFuelConsumptions = result.ToList();

How to calculate property in a chained linq query

Is it possible to compute 'on-the-fly' Division based on Department and JobTitle
with eventually applying some Transformations such as concatenating
in the chained linq query below ?
public static List<Developer> GetDevelopersData(List<Employee> employees)
{
List<Developer> developers =
employees.Where(x => x.Department == "Dev")
.Select(x => new Developer
{
Name = x.Name,
Department = x.Department,
JobTitle = x.Function,
Division = "Department" + "/" + "Function" // based on previous properties
}).ToList();
return developers;
}
I suppose Function and Departement are string type ?
List<Developer> developers =
employees.Where(x => x.Department == "Dev")
.Select(x => new Developer
{
Name = x.Name,
Department = x.Department,
JobTitle = x.Function,
Division = String.Concat(x.Function, "/", x.Department)
}).ToList();
return developers;
Unless I'm missing something, you could do:
Division = string.Format("{0}/{1}", x.Department, x.Function)
Assuming you just want the two properties concatenated into a string.
to compute on the fly, you can use let
(from x in employees
let MyFunction=String.Concat(x.Function, "/", x.Department)//you can use whatever you want here
select new Developer()
{
Name = x.Name,
Department = x.Department,
JobTitle = x.Function,
Division = x.MyFunction
}).ToList();

LINQ sorting anonymous types?

How do I do sorting when generating anonymous types in linq to sql?
Ex:
from e in linq0
order by User descending /* ??? */
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
If you're using LINQ to Objects, I'd do this:
var query = from e in linq0
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = e.Date.ToString("d")
} into anon
orderby anon.User descending
select anon;
That way the string concatenation only has to be done once.
I don't know what that would do in LINQ to SQL though...
If I've understood your question correctly, you want to do this:
from e in linq0
order by (e.User.FirstName + " " + e.User.LastName).Trim()) descending
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
Would this work, as a way of avoiding Jon's select...into?
from e in linq0
let comment = new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
orderby comment.User descending
select comment
I'm going to get a necromancer badge for this answer, but I still think it's worth showing this snippet.
var records = await (from s in db.S
join l in db.L on s.LId equals l.Id
where (...)
select new { S = s, Type = l.MyType }
).ToListAsync();
//Data is retrieved from database by now.
//OrderBy below is LINQ to Objects, not LINQ to SQL
if (sortbyABC)
{
//Sort A->B->C
records.OrderBy(sl => sl.Type, new ABC());
}
else
{
//Sort B->A->C
records.OrderBy(sl => sl.Type, new BAC());
}
ABC and BAC implement IComparer<MyType>.

Categories

Resources