'orderby' in linq using a string array c# - c#

Say I have a method definition as such:
public CustomerOrderData[] GetCustomerOrderData(string[] CustomerIDs)
{
var query = (from a in db.Customer
join b in db.Order on a.CustomerID equals v.CustomerID
orderby CustomerIDs
select new CustomerOrderData()
{
//populate props here
}).ToArray();
}
My CustomerIDs in input param could be {"1","3","400","200"}
I want my return array to be ordered in the above fashion. Is there an easy way to achive this?
My solution was to put it into a Dictionary and then create a new array while looping through my CustomerIDs collection.
CustomerOrderData does have a property named CustomerID

If you materialize the query, you should be able to find the index of the id in your array and use it as the ordering parameter. Shown below using extension methods.
var ordering = CustomerIDs.ToList();
var query = db.Customer.Join( db.Order, (a,b) => a.CustomerID == b.CustomerID )
.AsEnumerable()
.OrderBy( j => ordering.IndexOf( j.Customer.CustomerID ) )
.Select( j => new CustomerOrderData {
// do selection
})
.ToArray();

I think this solves problem:
var orderedArray = YourCustomArray.OrderBy(s => s).ToArray();

if the customerIds always will be numbers then cast it and order it before using it into ur query
var orderedIds = CustomerIDs.Cast<int>().OrderBy(x => x);

You could use IndexOf:
orderby ContactIds.IndexOf(a.CustomerId)
Note that this might not be efficient for large sets.

You could create a structure to lookup the desired index using the customerid.
string[] CustomerIDs;
Dictionary<string, int> customerIDOrdering = CustomerIds
.Select((c, i) => new (id = c.CustomerID, i = i})
.ToDictionary(x => x.id, x => x.i);
var query = from c in ...
orderby customerIDOrdering[c.CustomerID]
...

The join clause preserves the order of the outer sequence:
"The Join operator preserves the order of the outer sequence elements, and for each outer element, the order of the matching inner sequence elements."
http://msdn.microsoft.com/en-us/library/bb394939.aspx
So you should not need to orderby at all:
from orderIndex in CustomerIDs
join a in db.Customer on orderIndex equals a.CustomerID
join b in db.Order on a.CustomerID equals v.CustomerID

Related

Linq to DataSet

I want to pick up all Sellers that aren't boss of a department.
How can I make this? In this query below, only the Sellers that are bosses of a department are picked up, I want the opposite of thereof.
My query:
var query = db.Sellers
.Join(db.Departments,
s => s.Id,
d => d.BossId,
(s, d) => new { Seller = s, Department = d })
.Where(a => a.Seller.Id == a.Department.BossId) ????
.Select(x => x.Seller).ToList();
In the "Where" part, I tried a => a.Seller.Id != a.Department.BossId, but it's wrong I have 3 sellers that aren't bosses.
I tried with this way too:
var listNonBoss = (from s in db.Sellers
join d in db.Departments on s.Id equals d.BossId
select s.Id).ToList();
I want just the opposite of these queries.
Sometimes it's easier to break it into multiple steps.
First, get the collection of all boss IDs:
var bossIDs = db.Departments.Select(x => x.BossId);
Then get all sellers whose IDs are not in that collection:
var listNonBoss = db.Sellers.Where(x => !bossIDs.Contains(x.Id)).ToList();
Join in your code will do an inner join, meaning it'll filter out sellers who don't have a boss.
To do the opposite you can do an outer join, and then remove the ones who have a boss. In fluent LINQ an outer join is done by doing a GroupJoin and then SelectMany.
Something like this:
var query = db.Sellers
.GroupJoin(db.Departments, s => s.Id, d => d.BossId, (s, d) => new { Seller = s, Department = d })
.SelectMany(x => x.d.DefaultIfEmpty(), (seller, department) => new { s.seller, department})
.Where(a => a.department.BossId == null)
.Select(x => x.Seller).ToList();
Or, using query syntax:
var listNonBoss = (from s in db.Sellers
join d in db.Departments on s.Id equals d.BossId into joinedTable
from jt in joinedTable.DefaultIfEmpty()
where jt.BossId == null
select s.Id).ToList();

How to use descending with ascending in linq?

I have a query
SELECT * FROM member
ORDER BY d_no.member DESC, DateBirth ASC
I am thinking how can I do this in Linq?
I have a List<member>
I used this
Var Result = member.OrderByDescending(s => s.d_no).OrderBy(y => y.DateBirth ).ToList();
Is not working, any Idea?
OrderByDescending returns an instance of type IOrderedEnumerable<TSource>.
You can then call ThenBy or ThenByDescending to apply a second (or third) ordering constraint.
var Result = member.OrderByDescending(s => s.d_no)
.ThenBy(y => y.DateBirth )
.ToList();
Var Result = member.OrderByDescending(s => s.d_no,).ThenBy(y => y.DateBirth ).ToList();
Is there any reason to use method instead of query?
var result = (from m in member
order by m.d_no descending, m.DateBirth ascending
select m).ToList();

SQL left outer join, groupby & orderby to LINQ method query or method syntax

The main objective is to return a list of ingredients in a sorted order according to popularity (which is indicated by how many people have as their favorite a particular ingredient). The SQL equivalent is below:
select distinct COUNT(PersonalInfoes.FavoriteIngredient_ID) as NoUsing,
Ingredients.Name
from PersonalInfoes right join Ingredients
on PersonalInfoes.FavoriteIngredient_ID = Ingredients.ID
group by Ingredients.Name
order by NoUsing desc
What is the equivalent SQL query of method syntax that will produce the same result as above?
The closest I've got is this:
from i in db.Ingredients
join p in db.PersonalInfos on i.ID equals p.FavoriteIngredient.ID into ing
from p in ing.DefaultIfEmpty()
group i by i.Name into g
from p in g
orderby g.Count() descending
select new { Name = g.Key, Count = g.Count() }.Name;
Your query does a left outer join to include ingredients with no favorites. The problem is, for each ingredient with no favorites, you are counting the single value that gets produced by DefaultIfEmpty(). You can exclude the default values when counting like this:
orderby g.Count(x => x != null) descending
You actually don't have to perform a left join. You can just sum the counts of the groups from your group join:
from i in db.Ingredients
join p in db.PersonalInfos on i.ID equals p.FavoriteIngredient.ID into faves
group faves.Count() by i.Name into faveCounts
let count = faveCounts.Sum()
orderby count descending
select new { Name = faveCounts.Key, Count = count };
A subjective problem with your code is it's very confusing with all of the group by and from clauses. If you find this to be an issue, you can try using more descriptive names, using subqueries, or using method syntax.
Edit: Here is a method syntax version. It is not a direct conversion from the query syntax version because that would be quite ugly.
db.Ingredients
.GroupJoin(db.PersonalInfos,
i => i.ID,
p => p.ID,
(i, g) => new { i.Name, FaveCount = g.Count() })
.GroupBy(x => x.Name,
(key, grp) => new { Name = key, Count = grp.Sum(y => y.FaveCount) })
.OrderByDescending(x => x.Count);

modify linq to get first 5 elements

var lastArticles = from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name};
I need to get the first 5 elements.
I'm doing it by
.Take(5)
but is there a way to do in in the linq statement?
No, you need to use Skip() and Take() as method calls. There is no LINQ-specific equivalent.
var lastArticles = (from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name }).Take(5);
A linq Query should always be separate from the products of running that query.
.Take() produces results, and thus should be separate and distinct from the query.
//data query
var lastArticlesQuery = from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name};
//results of that query at this time
var lastArticles = lastArticlesQuery.Take(5);
This code is only syntatic sugar, utlimately it will be converted to a LINQ-methods chain that will look something like:
var lastArticles = be.MyTable
.Where(a => a.id == 1)
.Join(be.OtherTable, a => a.parent, c => c.id,
(a, c) => new { a, c})
.OrderByDescending(#t => #t.a.timestamp)
.Select(#t => new { #t.a, cName = #t.c.name });
So having a keyword for Take() would only add to the sytactic sugar and it would need to be re-converted as well.
In short, no, the only way is to use the Take() method.

LINQ to SQL: GroupBy() and Max() to get the object with latest date

Consider a SQL Server table that's used to store events for auditing.
The need is to get only that latest entry for each CustID. We want to get the entire object/row. I am assuming that a GroupBy() will be needed in the query. Here's the query so far:
var custsLastAccess = db.CustAccesses
.Where(c.AccessReason.Length>0)
.GroupBy(c => c.CustID)
// .Select()
.ToList();
// (?) where to put the c.Max(cu=>cu.AccessDate)
Question:
How can I create the query to select the latest(the maximum AccessDate) record/object for each CustID?
I'm wondering if something like:
var custsLastAccess = db.CustAccesses
.Where(c.AccessReason.Length>0)
.GroupBy(c => c.CustID)
.Select(grp => new {
grp.Key,
LastAccess = grp
.OrderByDescending(x => x.AccessDate)
.Select(x => x.AccessDate)
.FirstOrDefault()
}).ToList();
you could also try OrderBy() and Last()
Using LINQ syntax, which I think looks cleaner:
var custsLastAccess = from c in db.CustAccesses
group c by c.CustID into grp
select grp.OrderByDescending(c => c.AccessDate).FirstOrDefault();
Here: this uses max rather than OrderByDesc, so should be more efficient.
var subquery = from c in CustAccesses
group c by c.CustID into g
select new
{
CustID = g.Key,
AccessDate = g.Max(a => a.AccessDate)
};
var query = from c in CustAccesses
join s in subquery
on c.CustID equals s.CustID
where c.AccessDate == s.AccessDate
&& !string.IsNullOrEmpty(c.AccessReason)
select c;

Categories

Resources