I have a simple model that I'm trying to group:
public class PracticeArea
{
[Key]
public int PracticeAreaID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
I'd like to group by Type, how can I convert this:
var practiceAreas = (from c in db.PracticeAreas
select c).
to:
public Dictionary<string, string> groupedPracticeAreas { get; set; }
I'm not sure how grouping works within Linq - I can run .ToGroup(),. but that doesn't give me my dictionary.
I've tried:
practiceAreas = practiceAreas.ToDictionary(x => x.Type, x => x.Name);
But that gave me a cast error
This should not throw cast exception if both type and name are strings:
practiceAreas.ToDictionary(x => x.Type, x => x.Name)
But this would throw if there is more than one practice area exist for some type. You can use one of following options:
1) Use lookup instead of dictionary. It will create lookup for names by area type
practiceAreas.ToLookup(pa => pa.Type, pa => pa.Name)
2) Use dictionary with collection to hold all names for each type, e.g. Dictionary<string, List<string>> :
practiceAreas.GroupBy(pa => pa.Type)
.ToDictionary(g => g.Key, g => g.Select(pa => pa.Name).ToList())
3) Join all names in single string
practiceAreas.GroupBy(pa => pa.Type)
.ToDictionary(g => g.Key, g => String.Join("," g.Select(pa => pa.Name)))
Related
Given the following Models
public class ApiImageModel
{
public int ID { get; set; }
...
public List<TagModel> Tags { get; set; } = new();
}
and
public class TagModel
{
public int ID { get; set; }
...
public string Name { get; set; }
public List<ApiImageModel> Images { get; set; } = new();
}
How to query a list of ApiImageModel based on a given set of TagModels using Linq?
I am struggling with this for a while now and I'm certainly missing something basic but I can't put a pin on it.
I tried this approach for EF6:
EF6 How to query where children contains all values of a list
like so, holding all TagModel-IDs in an array "tagIDs":
int[] tagIDs;
...
IQueryable<ApiImageModel> images = context.Images.Where(image => tagIDs.All(id => image.Tags.Any(tag => tag.ID == id)));
But visual studio rewards me with an "InvalidOperationException":
The LINQ expression 'DbSet<ApiImageModel>()
.Where(a => __tagIDs_0
.All(id => DbSet<Dictionary<string, object>>("ApiImageModelTagModel")
.Where(a0 => EF.Property<Nullable<int>>(a, "ID") != null && object.Equals(
objA: (object)EF.Property<Nullable<int>>(a, "ID"),
objB: (object)EF.Property<Nullable<int>>(a0, "ImagesID")))
.Join(
inner: DbSet<TagModel>(),
outerKeySelector: a0 => EF.Property<Nullable<int>>(a0, "TagsID"),
innerKeySelector: t => EF.Property<Nullable<int>>(t, "ID"),
resultSelector: (a0, t) => new TransparentIdentifier<Dictionary<string, object>, TagModel>(
Outer = a0,
Inner = t
))
.Any(ti => ti.Inner.ID == id)))' could not be translated.
I'd be glad for some help :)
Assuming that your tags tagIDs are unique, you can do the following:
int[] tagIDs;
var tagCount = tagIDs.Length;
...
var images = context.Images
.Where(image => image.Tags.Where(tag => tagIDs.Contains(tag.ID)).Count() == tagCount);
Here we use Contains to grab tags in which we are interested and if their Count() is equal to tagIDs.Length - all tags are present in image's Tags relation.
LINQ expert how to convert this to linq.
Public Class Item
{
Public string Name {get;set;}
Public string Value {get;set;}
Public bool IsDis {get;set;}
}
I have collection which is like this
{
Name ="ABC", Value="x", IsDis= true
Name ="ABC", Value="y", IsDis=false
Name ="ABC1", Value="1", IsDis false
Name ="ABC1", Value="2", IsDis= true
}
As Name property has list of value. so I have to write LINQ query like this which return
Dictionary <string, List<Item>> = collection??????
GroupBy by Name and then collect it into a Dictionary.
Dictionary<string, List<Item>> resultSet = collection
.GroupBy(e => e.Name)
.ToDictionary(g => g.Key, g => g.ToList());
I have a list of a class that looks like this
public class MyClass
{
public ComplexType A {get;set;}
public ComplexTypeB B {get;set;}
}
var myList = new List<MyClass>();
I then have a target Dto which looks like this
public class MyTargetDto
{
public ComplexType A {get;set;}
public List<ComplexTypeB> ListOfB {get;set;}
}
It's very similar only that myTargetDto supports grouping by ComplexType
Given a flat list of MyClass, how can I (using Linq) convert it to a target list of MyTargetDto?
You should do something like this.
myList.GroupBy(x=>x.A)
.Select(x=> new MyTargetDto()
{
A= x.Key,
ListOfB = x.Select(s=>s.B).ToList()
});
If your ComplexType does not have an own implementation of Equals than at first you would need to implement an IEqualityComparer<ComplexType>:
public class Comparer : IEqualityComparer<ComplexType>
{
public bool Equals(ComplexType x, ComplexType y)
{
// code to check your complex type for equality
}
public int GetHashCode(ComplexType obj)
{
return obj.GetHashCode();
}
}
Then you can use this comparer to group your list using GroupBy:
List<MyClass> flatList = ...
List<MyTargetDto> result = flatList.GroupBy(e => e.A, e => e.B, new Comparer())
.Select(g => new MyTargetDto {
A = g.Key,
ListOfB = g.ToList()});
.ToList();
If ComplexType already has an own implementation of Equals that works appropriatly, than you can ommit that comparer:
List<MyTargetDto> result = flatList.GroupBy(e => e.A, e => e.B)
.Select(g => new MyTargetDto {
A = g.Key,
ListOfB = g.ToList()})
.ToList();
The first lambda of GroupBy selects the element by which the list is grouped. This will then be the Key property in the resulting IGrouping.
The second lambda selects the elements that should be contained in that group.
The final Select creates for each group a MyTargetDto, setting it's A property to the ComplexType and creating the ListOfB.
myList.GroupBy(item => item.A)
.Select(group => new MyTargetDto
{
A = group.Key,
ListOfB = group.Select(item => item.B).ToList()
});
As already posted by others, a solution would be
myList.GroupBy(x => x.A, x => x.B)
.Select(g => new MyTargetDto()
{
A = g.Key,
ListOfB = g.ToList()
});
Just wanted you to see an existing shortcut using a GroupBy overload
myList.GroupBy(x => x.A, x => x.B, (key, g) => new MyTargetDto()
{
A = key,
ListOfB = g.ToList()
});
Im trying to GroupBy a field, Select keys into a new type (Country), then SelectMany into a new collection type (CountryPrefix)
Through intuition i've come up with the following, however , i'm having trouble "sealing the deal"
given the following class
public class TempPrefix
{
public String CountryName { get; set; }
public String Prefix { get; set; }
public int ClassificationId { get; set; }
}
and tempPrefixes is a List<TempPrefix>
var countries = tempPrefixes
.GroupBy(x => x.CountryName)
.Select(x => new Country
{
Name = x.Key,
CountryPrefixes = x.SelectMany(y => new CountryPrefix
{
ClassificationId = y.ClassificationId,
Prefix = y.Prefix
})
});
Compile Error on SelectMany
The type arguments for method
'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable,
System.Func>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
I'm sure this is telling me something however i'm not quite sure what it is
ANSWER
A stated in the accepted answer i just needed to use Select and not SelectMany
Additionally i had to convert the result to a list
var countries = tempPrefixes
.GroupBy(x => x.CountryName)
.Select(x => new Country
{
Name = x.Key,
CountryPrefixes = x.Select(y => new CountryPrefix
{
ClassificationId = y.ClassificationId,
Prefix = y.Prefix
}).ToList()
});
Try changing that to Select instead..
In
CountryPrefixes = x.SelectMany(y => new CountryPrefix
{
ClassificationId = y.ClassificationId,
Prefix = y.Prefix
})
x is already a collection of TempPrefix, under the respective group, so you can simply get the CountryPrefix by a Select
I have two classes which share two common attributes, Id and Information.
public class Foo
{
public Guid Id { get; set; }
public string Information { get; set; }
...
}
public class Bar
{
public Guid Id { get; set; }
public string Information { get; set; }
...
}
Using LINQ, how can I take a populated list of Foo objects and a populated list of Bar objects:
var list1 = new List<Foo>();
var list2 = new List<Bar>();
and merge the Id and Information of each into a single dictionary:
var finalList = new Dictionary<Guid, string>();
Thank you in advance.
Sounds like you could do:
// Project both lists (lazily) to a common anonymous type
var anon1 = list1.Select(foo => new { foo.Id, foo.Information });
var anon2 = list2.Select(bar => new { bar.Id, bar.Information });
var map = anon1.Concat(anon2).ToDictionary(x => x.Id, x => x.Information);
(You could do all of this in one statement, but I think it's clearer this way.)
var finalList = list1.ToDictionary(x => x.Id, y => y.Information)
.Union(list2.ToDictionary(x => x.Id, y => y.Information))
.ToDictionary(x => x.Key, y => y.Value);
Make sure the ID's are unique. If not they will be overwritten by the first dictionary.
EDIT: added .ToDictionary(x => x.Key, y => y.Value);