Merge Two Lists of Lists - c#

I would like to merge two Lists of Lists of a certain class that share the same key
Let's say that I have the class :
public class Album {
public string Name { get ; set; }
public string Genre{ get ; set; }
}
and two Lists of Lists :
public List<List<Album>> AlbumList1 ;
public List<List<Album>> AlbumList1 ;
I would like to merge the lists in AlbumList1 and AlbumList2 that have the same Key .
For example if a List is called "Genre1" and another is called "Genre1" i would like to merge those two lists to create a unique list .
How can I perform this ?

It would probably look a bit like this:
var results = albumList1
.SelectMany(l => l)
.Concat(albumList2.SelectMany(l => l)
.GroupBy(l => l.Name, g => g.ToList())
.ToList();
Or perhaps like this:
var results = albumList1
.Join(albumList2,
l => l[0].Name,
l => l[0].Name,
(l1, l2) => l1.Concat(l2).ToList())
.ToList();
However, I'd also recommend you consider refactoring the code to use a IDictionary<string, IEnumerable<Album>> or an ILookup<string, Album> instead.

Seems like you might want to refactor that outer list a bit out to a class. Something like this:
public class Genre
{
public string Name { get; set; }
public List<Album> Albums { get; set; }
}
public class Album
{
public string Genre { get; set; }
public string Name { get; set; }
}
After that, you can then create a comparer (simplified)
public class GenreComarer : IEqualityComparer<Genre>
{
public bool Equals(Genre x, Genre y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(Genre obj)
{
return obj.Name.GetHashCode();
}
}
public class AlbumComarer : IEqualityComparer<Album>
{
public bool Equals(Album x, Album y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(Album obj)
{
return obj.Name.GetHashCode();
}
}
Then, it's a simple join - adding back in the missing Genres.
List<Genre> unified = list1.Join(list2,
e => e.Name,
e => e.Name,
(genre1, genre2) => new Genre
{
Name = genre1.Name,
Albums = genre1.Albums.Union(genre2.Albums, new AlbumComarer()).ToList()
}
).ToList();
unified.AddRange(list2.Except(list1, new GenreComarer()));
unified.AddRange(list1.Except(list2, new GenreComarer()));

Related

Mongo C# Driver how to do nested ElemMatch

If I have following architecture - how do I find zoos with noisy animals?
Example Data Model
public class Zoo
{
public List<Cage> Cages { get; set; }
}
public class Cage
{
public List<Animal> Animals { get; set; }
}
public abstract class Animal
{
public string Name { get; set; }
}
public class Feline : Animal
{
public int MeowsCount { get; set; }
}
public class Canine: Animal
{
public int BarksCount { get; set; }
}
For now I do extract whole db and find necessary with LINQ
public IEnumerable<Zoo> FindNoisyZoos(int soundsCount)
{
var allZoos = await zooCollection.Find(_ => true).ToListAsync();
List<Zoo> noisyZoos = allZoos.Where(z =>
z.Cages.Any(c =>
c.Animals.Where(a => ((Feline)a).MeowsCount == soundsCount || ((Canine)a).BarksCount == soundsCount))))
.ToList();
return noisyZoos;
}
And thats terribly inefficient. I would like being able to do nested ElemMatch, but I can't wrap my head around how ElemMatch works. I expect it to be something like this:
public IEnumerable<Zoo> FindNoisyZoos(int soundsCount)
{
var noisyFelineFilter = new FilterDefinitionBuilder<Animal>().And(new FilterDefinitionBuilder<Animal>().OfType<Feline>(), new FilterDefinitionBuilder<Animal>().Eq(a => ((Feline)a).MeowsCount == soundsCount));
var noisyCanineFilter = new FilterDefinitionBuilder<Animal>().And(new FilterDefinitionBuilder<Animal>().OfType<Canine>(), new FilterDefinitionBuilder<Animal>().Eq(a => ((Canine)a).BarksCount == soundsCount));
var noisyAnimalFilter = new FilterDefinitionBuilder<Animal>().Or(noisyFelineFilter, noisyCanineFilter);
// smth like this: new FilterDefinitionBuilder<Zoo>().ElemMatch(z => z.Cages.ElemMatch(c => c.Animals.ElemMatch(noisyAnimalFilter)));
var noisyFilter;
var zoos = await zooCollection.Find(noisyFilter).ToListAsync();
return zoos;
}
MongoDB .NET driver can process some LINQ queries server-side. It translates them under the hood into MongoDB native ones. Did you try something like zooCollection.AsQueriable().Where(z => z...).ToList(). .ToList() comes last and sends data to the client. If this works, the filtering will be performed server-side, if it doesn't the driver will tell you directly that it is not supported.

NHibernate parent list with child count

i am using NHibernate 4 with mysql. i have got 2 tables. My tables are cat and answer.
public class cat
{
[Key]
public virtual int id { get; set; }
public virtual string catName { get; set; }
public virtual IList<answer> answers { get; set; }
}
public class answer
{
[Key]
public virtual int id { get; set; }
public virtual int catId { get; set; }
public virtual string detail { get; set; }
public virtual bool stat { get; set; }
[ForeignKey("catId")]
public virtual cat cats { get; set; }
}
i want to select all cat record(with answer list) and with their cild answers count.
my sql query like that;
select count(t2.id) as count, cats.*from cat cats left join answer t2 ON(cats.id=t2.catId and t2.stat=0) GROUP BY(cats.id);
Result like this;
id - catName - count
1 - Book - 5
2 - Pc - 0
3 - English - 22
4 - Arts - 56
i have try also this NH query;
public class myClass {
public virtual int count { get; set; }
public virtual cat cats { get; set; }
}
var u = db.CreateCriteria(typeof(cat), "cats")
.CreateAlias("answer", "t2", NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("t2.stat", false))
.SetProjection(Projections.ProjectionList()
.Add(Projections.Count("t2.id"), "count")
.Add(Projections.Group<cat>(g => g.id)));
var list = u.SetFetchMode("answer", FetchMode.Eager)
.SetResultTransformer(Transformers.AliasToBean<myClass>())
.List<myClass>();
This NHibernate query return also answers count. but cats always return null. How can i do my query for this result ?
Edit 1
i can do it like that
public class myClass {
public virtual int count { get; set; }
public virtual catId count { get; set; }
public virtual cat cats { get; set; }
}
cat cats = null;
answer answers = null;
var u = db.QueryOver<cat>(() => cats)
.JoinQueryOver(x => x.answers, () => answers, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("answers.stat", false))
.SelectList(cv => cv
.SelectCount(() => answers.id)
.SelectGroup(c => c.id))
.List<object[]>()
.Select(ax => new myClass
{
count = (int)ax[0],
catId = (int)ax[1],
cats = (cat)db.QueryOver<cat>().Where(w=>w.id==(int)ax[1]).Fetch(fe => fe.answers).Eager.SingleOrDefault()
})
.ToList();
In your result cats is always null, because the ResultTransformer tries to map the properties by their names.
Please check your NHibernate logfile. You will probably see that your query returns the columns count and id, but myClass has the properties count and cats.
Edit:
New suggestion:
The previous suggestion did not work, because the property id is of type Int32 and cannot be assigned to myClass.cat (which is of type cat).
If you don't need a reference to a cat-object in myClass then you can change it to this:
public class myClass {
public virtual int count { get; set; }
public virtual int catId { get; set; }
}
and have the projection like this:
.Add(Projections.Group<cat>(g => g.id), "catId"));
If you do need a property of type cat in your result class I don't think this can be done with a simple projection but I might be wrong.
Edit 2:
Since you require an object of type cat in your result I suggest you put it together manually after the query, e.g.:
New result class:
public class myClassResult {
public virtual int count { get; set; }
public virtual cat cats { get; set; }
}
Add this after your query logic:
IList<myClassResult> result = new List<myClassResult>();
foreach (var idWithCount in list)
{
result.Add(new myClassResult()
{
cats = catsOnlyList.FirstOrDefault(x => x.id == idWithCount.catId),
count = idWithCount.count
});
}
catsOnlyList refers to a simple list of cats that you need to get beforehand. I know this isn't pretty but I don't think you can group by cat itself in the query.
Old suggestion (does not work because of incompatible types):
Instead of
.Add(Projections.Group<cat>(g => g.id)));
use
.Add(Projections.Group<cat>(g => g.id), "cats"));
i can do that query like that;
public class myClass {
public virtual int count { get; set; }
public virtual İnt catId { get; set; }
public virtual cat cats { get; set; }
}
cat cats = null;
answer answers = null;
var u = db.QueryOver<cat>(() => cats)
.JoinQueryOver(x => x.answers, () => answers, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("answers.stat", false))
.SelectList(cv => cv
.SelectCount(() => answers.id)
.SelectGroup(c => c.id))
.List<object[]>()
.Select(ax => new myClass
{
count = (int)ax[0],
catId = (int)ax[1],
cats = (cat)db.QueryOver<cat>().Where(w=>w.id==(int)ax[1]).Fetch(fe=>fe.answers).Eager.SingleOrDefault()
})
.ToList();

Trying to get a list of properties dynamically where count > 0

I have a model like so
public class UserModel
{
List<UserModel> users
}
public class UserModel
{
public List<UserSomeObj> userSomeObj { get; set; }
public List<UserSomeOtherObj> userSomeOtherObj { get; set; }
}
public class UserSomeObj
{
public int someIntProperty { get; set; }
public string someStringProperty { get; set; }
}
public class UserSomeOtherObj
{
public int someIntProperty { get; set; }
public string someStringProperty { get; set; }
}
Each UserModel class List is comprised of several other class Lists.
I am referencing them dynamically like so by looping over a list of targeted properties.
to get a list of properties matching the 'prop' variable;
var props = MethodToGetTargetedProperties();
// props example content would be a list of strings like so "UserSomeObj", "UserSomeOtherObj"
foreach (var prop in props)
{
var results = users.Select(x => x.GetPropertyValue(prop)).ToList();
//results contain lists of prop where count == 0 and i dont want them
}
what I am trying to do is reduce the results where count of the lists targeted is greater than 0 .... problem is that I can't find the correct order/syntax to get it to work.
Thanks
As List<T> implements the non-generic ICollection interface, you can cast to that:
var results = users.Select(x => x.GetPropertyValue(prop))
.Cast<ICollection>()
.Where(list => list.Count > 0)
.ToList();
You could do the cast within the Where if you want, although I prefer the above:
var results = users.Select(x => x.GetPropertyValue(prop))
.Where(list => ((ICollection) list).Count > 0)
.ToList();
Thanks ..... in reviewing your reply I was able to do the following
var results = users.Select(x => x.GetPropertyValue(prop))
.Cast<IEnumerable<object>>().Where(y => y.Count() > 0).ToList();
Then after looking at your answer more and trying to understand all its facets, I wondered if I could do it all in one line. The two List classes in UserModel have 2 common properties (idSomething and idSomeOtherThing) and since I will be combining them and doing a Distinct, i thought, hmm, one liner might be possible.

Get Distinct List using LINQ

I want to translate this query in LINQ format:
select m.MenuName,m.ParentID from Menu m where Id in(
select distinct m.ParentID from Menu m inner join MenuRole mr on mr.MenuID=m.Id)
This is what I have tried
var _employee = _db.Employees.AsEnumerable().Where(e => e.Id == Int32.Parse(Session["LoggedUserId"].ToString()))
.FirstOrDefault();
var _dashboardVM = new DashboardVM
{
MenuParentList = _employee.Designation.Role.MenuRoles
.Select(x => new SMS.Models.ViewModel.DashboardVM.MenuParent
{
MenuParentID=x.Menu.ParentID ,
MenuParentName=x.Menu.MenuName
})
.Distinct().ToList()
};
I am getting all list instead of distinct List
Dashboard VM
public class DashboardVM
{
public class MenuParent
{
public int? MenuParentID { get; set; }
public string MenuParentName { get; set; }
}
public List<MenuParent> MenuParentList { get; set; }
public List<Menu> MenuList { get; set; }
public User User { get; set; }
}
The Distinct() method checks reference equality for reference types. This means it is looking for literally the same object duplicated, not different objects which contain the same values.
Can you try the following? You may need to tweek as I have no testing environment:
MenuParentList = _employee.Designation.Role.MenuRoles.GroupBy ( r => r.Menu.ParentID + r.Menu.MenuName ).
.Select (y => y.First ())
.Select(x => new SMS.Models.ViewModel.DashboardVM.MenuParent
{
MenuParentID=x.Menu.ParentID ,
MenuParentName=x.Menu.MenuName
}).ToList();

Convert IGrouping from one class to another using linq

I have two classes Teams and PlayerTeams
public class PlayerTeams
{
public int ID { get; set; }
public string PlayerName { get; set; }
public string PlayerCountry { get; set; }
public bool IsActive { get; set; }
public string PlayerTeam { get; set; }
}
public class Players
{
public int ID { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public bool? Status { get; set; }
}
I have a list of PlayerTeams which is grouped by PlayerTeam like this.
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy( x => x.PlayerTeam)
.ToList();
Its of type List<IGrouping<string, PlayerTeams>> but I want it to be of type List<IGrouping<string, Players>> as I do not want the redundant key information on every row.
How could I possibly achieve that? I could only think of something like .ConvertAll() on the IGrouping. I am not able to make it also.
Is there an efiicient way to do this?
If you can change the grouping, I'd just use:
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.ToList();
Where Players.FromPlayerTeam is a static method in Players which takes a PlayerTeam and returns a Players.
Additionally, I'd suggest using ToLookup instead of GroupBy - a Lookup is exactly what you want here, without bothering with the ToList call.
This not testet, just an idea.
If you have trouble converting your linq statement, which is expecting the IGrouping type, to a string list, then you might have to select it before.
var groupedPlayers = new List<string>();
groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.Select(x => x.Key) // << added select
.ToList();

Categories

Resources