LinQ OrderBy specific element in sublist - linq

I have a Category class
public class Category
{
public long Id { get; set; }
public Translation[] Translations { get; set; }
}
and a Translation class
public class Translation
{
public string Locale { get; set; }
public string Name { get; set; }
}
I then have a List of Categories List<Category> that is populated with category objects. I want to order my unorderedCategoryList alphabetically by the Name field in Translation based on specified Locale.
So for example I have 3 categories:
{1, ["EN","aaa"], ["RU","zzz"]}
{2, ["EN","ccc"], ["RU","eee"]}
{3, ["EN","bbb"], ["RU","aaa"]}
Ordering them with specified locale EN would result in a list ordered like this:
{1, ["EN","aaa"], ["RU","zzz"]}
{3, ["EN","bbb"], ["RU","aaa"]}
{2, ["EN","ccc"], ["RU","eee"]}
and by RU like this:
{3, ["EN","bbb"], ["RU","aaa"]}
{2, ["EN","ccc"], ["RU","eee"]}
{1, ["EN","aaa"], ["RU","zzz"]}
This line of code is as far as I got but it doesn't seem to work :
var sortedList = unorderedCategoryList.OrderBy(go => go.Translations.Where(t => t.Locale == "EN").Select(t => t.Name)).ToList();

You could create a Custom Comparer for Category Type as follows.
public class CategoryComparer : IComparer<Category>
{
private string _locale;
public CategoryComparer(string locale)
{
_locale = locale;
}
public int Compare(Category x, Category y)
{
var orderedX = x.Translations.Where(c=>c.Locale.Equals(_locale)).OrderBy(c=>c.Name);
var orderedY = y.Translations.Where(c=>c.Locale.Equals(_locale)).OrderBy(c=>c.Name);
return orderedX.First().Name.CompareTo(orderedY.First().Name);
}
}
Now you could sort as
var sortByKey = "EN";
var orderedList = list.OrderBy(x=>x,new CategoryComparer(sortByKey));
Demo Code

Try this out:
var sortedList = unorderedCategoryList
.OrderBy(go => go.Translations.OrderByDescending(t => t.Name == "EN")).ToList();
Unverified but I pulled sources from here and here

Related

Get tuple and list linked as result

I have this query :
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct
}).Distinct();
which returns rows with 3 values.
ClientName and ClientNumber can be linked to multiple products.
So we can have :
NameA NumberA Product1
NameA NumberA Product2
NameA NumberA Product3
NameB NumberB Product4
NameC NumberC Product5
I would like to know if it is possible to store that in a List of a certain class which would be like :
class MyClass
{
string ClientName,
int ClientNumber,
List<int> ClientProducts
}
So there are no duplicate of ClientName and ClientNumber.
Thank you in advance.
With this class structure to represent your data:
class MyClass
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public List<int> ClientProducts { get; set; }
}
class Procuct
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public int ProductID { get; set; }
}
and this test data:
List<Procuct> Products = new List<Procuct>()
{
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 1},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 2},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 3},
new Procuct() { ClientName = "B", ClientNumber = 2, ProductID = 4},
new Procuct() { ClientName = "C", ClientNumber = 2, ProductID = 5}
};
you can use the following linq query:
var q = from p in Products
group p by new
{
cName = p.ClientName,
cNumber = p.ClientNumber
} into pGroup
select new MyClass
{
ClientName = pGroup.Key.cName,
ClientNumber = pGroup.Key.cNumber,
ClientProducts = pGroup.Select(x => x.ProductID).ToList()
};
to get exactly what you want, i.e. a collection of MyClass objects.
The Grouping performed in the above linq query essentially guarantees that there will be no duplicates on (ClientName, ClientNumber).
Since you mention Linq-to-sql, most probably you Client entity already has the products linked. You might look for an overcomplicated solution.
It depends a bit on your foreign key stucture, but if your datamodel would be
Client has 1-many product and you have a Foreign key from product to client it is already present.
So you can just reference client.Products.
So in your case it would be
var query = (from Clients...
where ...
select new
{
ClientName = Client.ClientName,
ClientNumber = Client.ClientNumber,
ClientProduct = Client.Products.Select(s=>s.id).ToList()
});
But you might as well simply use your client entity with a eager load of the products.
It all depends on your datamodel + proper foreign key structure
if you have a many-many associations like Product-per-client between your client and product you can start from that entity. Have a look at this documentation - it provides a good starting point for Linq-2-sql.
http://weblogs.asp.net/scottgu/using-linq-to-sql-part-1
I solve same problem , I think it useful to you
Only check your Where Condition properly
Thank...
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct.ToList()
}).Distinct();

How to get ASP.Net Web API and OData to bind a string value as a key?

I'm going through a short Web Api + OData tutorial from asp.net: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/getting-started-with-odata-in-web-api/create-a-read-only-odata-endpoint.
I downloaded the example project, and it works. But then I started playing around with the Product model that they use in the example. I added a new property to act as a key of type string instead of an integer key.
The new Product.cs:
public class Product
{
public string stringKey { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
The modified controller:
public class ProductsController : EntitySetController<Product, string>
{
static List<Product> products = new List<Product>()
{
new Product() { stringKey = "one", ID = 1, Name = "Hat", Price = 15, Category = "Apparel" },
new Product() { stringKey = "two", ID = 2, Name = "Socks", Price = 5, Category = "Apparel" },
new Product() { stringKey = "three", ID = 3, Name = "Scarf", Price = 12, Category = "Apparel" },
new Product() { stringKey = "four", ID = 4, Name = "Yo-yo", Price = 4.95M, Category = "Toys" },
new Product() { stringKey = "five", ID = 5, Name = "Puzzle", Price = 8, Category = "Toys" },
};
[Queryable]
public override IQueryable<Product> Get()
{
return products.AsQueryable();
}
protected override Product GetEntityByKey(string key)
{
return products.FirstOrDefault(p => p.stringKey == key);
}
}
The trouble is that when I go to /odata/Products(one) the string "one" is not bound to the key argument in the GetEntityByKey(string key) action. However, when I browse to odata/Products(1) then "1" does get bound to the key argument.
How can I get a string with text values to bind correctly, instead of just binding strings with numerical values?
Update
I forgot to include the WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
}
}
I noticed that the path /odata/Products(0011-1100) would only bind "0011" as the string key.
After some playing around with it, I've found that the following path works as I had hoped:
/odata/Products('one')
It appears the single quotes are required to read the entire string within the parentheses.

select and update based on a child objects property minimum value using Linq

I have a Type Supplier that has a property SupplierId and a another property NearestLocation which is a of Type SupplierLocation, the SupplierLocation consists of properties SupplierId and DistanceFromDevice
class Supplier
{
public int SupplierId { get; set; }
public SupplierLocation NearestLocation { get; set; }
}
class SupplierLocation
{
public int SupplierId { get; set; }
public decimal DistanceFromDevice { get; set; }
public double Longitude { get; set; }
public double Latitude {get; set;}
}
I have a List of all my Supplierlocations a supplier can have a n number of locations. I have also calculated the DistanceFromDevice property for each location.
I have a List whose id's can be found in the SupplierLocations List.
What I would like to do using linq is to join my supplier to the SupplierLocation by the SupplierId and populate the NearestLocation Property of the Supplier class with the the Location that has the least DistanceFromDevice value of all the locations for that particular supplier.
Hope this makes sense. Can this be done using linq.
Many thanks in advance.
Paul
Here is a working example in LINQPad
void Main()
{
var suppliers = new List<Supplier>
{
new Supplier() {SupplierId = 1},
new Supplier() {SupplierId = 2},
new Supplier() {SupplierId = 5}
};
var locations = new List<SupplierLocation>
{
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 10, Latitude = 1, Longitude = 2},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 20, Latitude = 1, Longitude = 3},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 30, Latitude = 1, Longitude = 4},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 40, Latitude = 1, Longitude = 5},
new SupplierLocation {SupplierId = 2, DistanceFromDevice = 10, Latitude = 2, Longitude = 2},
new SupplierLocation {SupplierId = 2, DistanceFromDevice = 20, Latitude = 2, Longitude = 3},
new SupplierLocation {SupplierId = 3, DistanceFromDevice = 10, Latitude = 3, Longitude = 2}
};
var result = from s in suppliers
join l in locations on s.SupplierId equals l.SupplierId
into grp
where grp.Count() > 0
select new Supplier() { SupplierId = s.SupplierId, NearestLocation = grp.OrderBy (g => g.DistanceFromDevice).First()};
result.Dump();
}
class Supplier
{
public int SupplierId { get; set; }
public SupplierLocation NearestLocation{ get; set; }
}
class SupplierLocation
{
public int SupplierId { get ;set; }
public decimal DistanceFromDevice { get; set; }
public double Longitude { get; set; }
public double Latitude {get; set;}
}
So, you want to set NearestLocation on Supplier where the SupplierId is equal to one in List<SupplierLocation>?
Assume you have a List<SupplierLocation> named "Locations" and "currentSupplier" is the Supplier you want to assign the NearestLocation of:
try
{
var minForSupplier = Locations.Where(x => x.SupplierId == currentSupplier.SupplierId).Min(x => x.DistanceFromDevice);
currentSupplier.NearestLocation = Locations.Where(x => x.SupplierId == currentSupplier.SupplierId && x.DistanceFromDevice == minForSupplier).Single();
}
catch(InvalidOperationException e)
{
// There is more than one SupplierLocation
}

How do I combine columns together

I have 2 lists that contain different data but have similar columns.
Basically I want to join these lists but then merge the similar columns into 1.
var listCombo= List1.Where(a=>DataIds.Contains(a.dataId)).DefaultIfEmpty()
.Join(List2,
list1 => list1.key,
list2 => list2.key,
(L1,L2) => new
{
L2.key,
L2.dataId,
L2.dataValue,
L2.date,
L1.secId,
L1.dataId,
L1.dataValue
});
I'd like to combine the dataId and dataValue columns together. How do I do that?
So I can just say listCombo.dataId or listCombo.dataValue rather than having to uniquely name them.
If I understand your question, you're trying to combine the two fields into one. Assuming that you have a Class
public class List1 {
public int dataId {get;set;}
public string dataValue {get;set;}
public string combinedValue {get;set;}
}
No you can use like,
var listCombo= List1.Where(a=>DataIds.Contains(a.dataId)).DefaultIfEmpty()
.Join(List2,
list1 => list1.key,
list2 => list2.key,
(L1,L2) => new List1
{
dataId = L2.dataId,
dataValue = L2.dataValue
combinedValue = L2.dataId + " - " L2.dataValue
});
I would use POCO methods for a list1, list2, and a third list which is a combo of both. I would be sure that if you are combining unlike types you do appropriate casts or if you want complex types you can define them as their own properties or what not.
static void Main(string[] args)
{
List<A> lisA = new List<A> {new A {AId = 1, AName = "Brett"}, new A {AId = 2, AName = "John"}};
List<B> lisB = new List<B> {new B { BId = 1, BName = "Doe" }, new B { BId = 2, BName = "Howard" } };
List<C> lisC = lisA.Join(lisB,
list1 => list1.AId,
list2 => list2.BId,
(L1, L2) => new C
{
CId = L1.AId,
CName = L1.AName + " " + L2.BName
}).ToList();
lisC.ForEach(
n => Console.WriteLine(n.CName + "\n")
);
}
public class A
{
public int AId { get; set; }
public string AName { get; set; }
}
public class B
{
public int BId { get; set; }
public string BName { get; set; }
}
public class C
{
public int CId { get; set; }
public string CName { get; set; }
}

linq aggregate

class Category
{
public string Name { get; set; }
public int Count { get; set;}
}
Name Count
AA 2
BB 3
AA 4
I have an IEnumerable<Category>
and would like to get a list of Categories with unique names and the sum of multiple entries
Output
Name Count
AA 6
BB 3
Update
class Category
{
public string Name { get; set; }
public int CountA { get; set;}
public int CountB { get; set;}
public string Phone { get; set;}
}
How would I sum two columns. and the phone column can be the last row or any row
Your updated question isn't entirely clear in terms of the phone number, but I suspect you want something like:
var query = from category in list
group category by category.Name into grouped
select new { Name = grouped.Key,
SumA = grouped.Sum(x => x.CountA),
SumB = grouped.Sum(x => x.CountB),
Phone = grouped.Last().Phone };
Changing grouped.Last() to grouped.First() would be more efficient, by the way.
Evaluating multiple aggregates in this way isn't terribly efficient in general. The Push LINQ project developed by myself and Marc Gravell makes it a lot more efficient at the cost of not being quite as easy to use. You might want to look into it if you need to deal with a lot of data.
var foo = new List<Category>() {
new Category() { Name = "AA", Count = 2},
new Category() { Name = "BB", Count = 3},
new Category() { Name = "AA", Count = 4}
};
var bar = foo.GroupBy(c => c.Name).Select(g => new Category(){ Name = g.Key, Count = g.Sum(c => c.Count) });

Resources