I have data that looks like so:
UserId | SongId
-------- --------
1 1
1 4
1 12
2 95
I also have the following class:
class SongsForUser
{
public int User;
public List<int> Songs;
}
What I would like to do is use LINQ to select from my data to create a collection of SongsForUser objects. Below is what I have come up with so far:
var userCombos = songs.UserSongs.Select(x => new SongsForUser() { User = x.UserId,
Songs = /*What goes here?*/ });
How would I go about populating my Songs List?
So the result should be two SongsForUser objects. For user 1 it would have 3 items in the Songs list. For user 2 it would have 1 item in the Songs list.
songs.UserSongs.GroupBy(x => x.User).Select(g => new SongsForUser()
{
User = g.Key,
Songs = g.Select(s => s.SongId).ToList()
});
I suspect you want:
var songsByUser = songs.UserSongs
.GroupBy(song => song.UserId, song => song.SongId)
.Select(g => new SongsForUser { User = g.Key,
Songs = g.ToList() });
To explain, after the GroupBy you'll have a bunch of groups, where the key of each group is the user ID, and the values within the group are the song IDs:
Key = 1, Values = 1, 4, 12
Key = 2, Value = 95
Then you're just converting that into your SongsForUser type. Note that you don't need to explicitly include the () when calling the constructor in an object initializer - it's implicit unless you need to specify constructor arguments.
You could do this all in one GroupBy call, by the way:
var songsByUser = songs.UserSongs
.GroupBy(song => song.UserId, song => song.SongId,
(user, ids) => new SongsForUser { User = user,
Songs = ids.ToList() });
Personally I usually find a separate Select call to be more readable.
You can also do all of this with a query expression:
var songsByUser = from song in songs.UserSongs
group song.SongId by song.UserId into g
select new SongsForUser { User = g.Key, Songs = g.ToList() };
EDIT: The above is "provider-neutral" but it sounds like it's not working with LINQ to Entities. You may be able to get it to work like this:
var songsByUser = songs.UserSongs
.GroupBy(song => song.UserId, song => song.SongId)
.AsEnumerable()
.Select(g => new SongsForUser { User = g.Key,
Songs = g.ToList() });
The AsEnumerable call will force the grouping to be done in the database, but the final projection (including the ToList call) to be done locally. You should check the generated SQL for efficiency though.
Lets say you have the following:
public class SongsForUser
{
public int UserId;
public List<int> Songs;
}
Then a function like this one here will do. The list is just there to have
some data to test with.
public void Group()
{
List<Tuple<int, int>> SongRelations = new List<Tuple<int, int>>();
SongRelations.Add(new Tuple<int, int>(1, 1));
SongRelations.Add(new Tuple<int, int>(1, 4));
SongRelations.Add(new Tuple<int, int>(1, 12));
SongRelations.Add(new Tuple<int, int>(2, 95));
var list = SongRelations.GroupBy(s => s.Item1)
.Select(r => new SongsForUser()
{
UserId = r.Key,
Songs = r.Select(t => t.Item2).ToList(),
});
}
list contains 2 items of type SongsForUser afterwards.
One with user 1 and a list of songs containing 1, 4 and 12
and one with user 2 and a list of songs containing 95.
In its simplest form you can just:
List<MapPoint> points = db.PropertyResearches.Where(a => a.deptId == 66).Select(x => new MapPoint { property = x.notes.Substring(0, 10), latitude = x.lat, longitude = x.#long }).ToList();
Related
I have an array of a class that is representing a user and another array of a class that is representing pinned items (currently only user ids). Here are the classes:
public class User
{
public int UserId { get; set; }
public bool Pinned { get; set; }
public User(int userId, bool pinned)
{
UserId = userId;
Pinned = pinned;
}
}
public class PinnedItem
{
public int UserId { get; set; }
public PinnedItem(int userId)
{
UserId = userId;
}
}
All user ids of pinned users are saved in a specific order (order of pinned items) and I want to order the array of users so that pinned users are on top and those pinned users follow the order of pinned items array. So for example, if I have an array of users like:
var users = new []{ new User(1, true), new User(2, false), new User(3, true) }
and a pinned items array that looks like this:
var pinnedItems = new [] { new PinnedItem(3), new PinnedItem(1) }
then I want the resulting array to look like this:
[ {3, true}, {1, true}, {2, false} ]
I also need it to work if the array of pinned items isn't in any kind of order. So if I have this array of users:
var users = new []{ new User(1, false), new User(2, true), new User(3, true), new User(4, true) }
and this array of pinned items:
var pinnedItems = new [] { new PinnedItem(3), new PinnedItem(2), new PinnedItem(4) }
in this case I want the resulting array to look like this:
[ {3, true}, {2, true}, {4, true}, {1, false} ]
Any kind of help will be very much appreciated. Also if there is anything unclear in the question, I am sorry and will edit it accordingly if need be.
It's a bit scruffy, but something like this will do it:
var joined =
users
.GroupJoin(pinnedItems, u => u.UserId, p => p.UserId, (u, p) => new { u.UserId, Pinned = p.Any() })
.OrderByDescending(r => r.Pinned)
.ThenByDescending(r => r.UserId)
.ToList();
You can tune the projections and sorting to get it just how you want it.
Im sure there are alot of ways to do this, I have alot of LINQ left to learn but the following should get you started;
// First, get the users that are mentioned by a PinnedItem
var pinnedUsers = pinnedItems.Select(x => users.FirstOrDefault(y => y.UserId == x.UserId));
// Get all the users that are not mentioned in a PinnedItem
var unpinnedUsers = users.Except(pinnedUsers);
// Combine both
var both = pinnedUsers.Concat(unpinnedUsers);
Here is a solution optimized for bigger arrays that my mentor came up with if anyone will ever stumble upon this post (the answer posted by #Fixation is totally fine if you know there won't be many pinned items):
Dictionary<int, int?> positionByUserId = pinnedItems
.Select((i, index) => new { i.UserId, Position = index })
.ToDictionary(x => x.UserId, x => (int?)x.Position);
var result = users
.Select(u => new
{
User = u,
Position = positionByUserId.GetValueOrDefault(u.UserId) ?? int.MaxValue
})
.OrderBy(x => x.Position)
.Select(x => x.User)
.ToArray();
This is an entity
public class ViewModel
{
public string Id { get; set; }
public DateTime Start { get; set; }
public string Name { get; set; }
}
This is my context query,works with ef core in dbcontext.
var list = _Service.GetDataByMonth(start,end).ToList();
// it returns all entity between giving start, end param.
// start and end is Datetime property comes from ajax. This code works fine it return almost 1k objectlist with type of ViewModel
like
[0] Id="1",Title="sample",Start:""15.12.2020"
[1] Id="2",Title="sample2",Start:""15.12.2020"
[2] Id="3",Title="sample3",Start:""16.12.2020"
[3] Id="4",Title="sample4",Start:""16.12.2020"
As shows above we got almost 20+ entity per day.
I can get count per day like
var listt = _Service.GetDataByMonth(start,end).GroupBy(x => x.Start.Date).Select(grp => new { Date = grp.Key, Count = grp.Count() });
[0] Key:""15.12.2020",Count:20
[1] Key:""16.12.2020",Count:25
[2] Key:""17.12.2020",Count:44
it returns like this.
So what i want is giving start and end param a funciton then get 3 values per day between giving datetime object
NEW
var list1= _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date)
.Select(grp => grp.Take(3)).ToList();
//this type List<Ienumerable<Viewmodel>>
var list2 = _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date).Select(grp => grp.Take(3).ToList()).ToList();
// this type List<List<Viewmodel>>
My want it List<Viewmodel>
service
...
return entity.Where(x =>
x.IsDeleted == false &&
(x.StartDate.Date >= start.Date && x.StartDate.Date <=end.Date)
).OrderBy(x => x.FinishDate).ToList();
// it work with this way but bad way
var lis = list.SelectMany(d => d).ToList();
Yes I figuredout using selectmany instead of select works fine. Thank you again
You can use .Take() to only take 3 items of each group:
_Service.GetDataByMonth(start,end)
.GroupBy(x => x.Start.Date)
.Select(grp => new { Data = grp.Take(3).ToList(), Date = grp.Key, Count = grp.Count() })
.ToList();
var list1= _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date)
.Select(grp => grp.Take(3)).ToList();
It returns List<List> maybe it will help some one bu my need is comes from with this code
This code makes list of list each element is list and contians 3 object.
var list1= _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date)
.SelectMany(grp => grp.Take(3)).ToList();
Many makes just list. It mades list with ordered objects
What is the LINQ Equivalent of
Select DISTINCT A, B, C from TESTDB WHERE ALPHA =1
I am trying something like this:
var data = TESTDB.WHERE(i=>i.ALPHA==1).SELECT(A,B,C).DISTINCT();
Using anonymous objects will do the trick:
var data = TESTDB.Where(i => i.ALPHA == 1).Select(i => new {i.A, i.B, i.C}).Distinct();
To retain the model:
List<Book> books = db.Book.Select(i => new Book {Author = i.Author, Title = i.Title}).Distinct().ToList();
You can also try
db.Table
.OrderBy(m=>m.Name)
.DistinctBy(m=> new{m.SerialNumber, m.Manufacturer})
.ToList();
If you use it like that:
var list = new List<Pet>()
{
new Cat() {Name = "Kitty", Id = 1},
new Cat() {Name = "Kitty", Id = 1},
new Cat() {Name = "Kitty", Id = 1}
};
var distinctCount = list.Where(i => i.Id == 1).Distinct().Count();
it turns out that distinctCount equals 3. Why is that? Seems that by default Distinct distinguishes between instances (even though all properties have the same values they're three instances).
You should implement custom comparer, here you'll find the code example: http://msdn.microsoft.com/en-us/library/bb338049.aspx.
Yet I'm not sure why do you want to select three properties (A,B,C). You can access single property in this way:
var data = list.Where(i => i.Id == 1).Distinct().SelectMany(i => i.Name);
However in order to select multiple properties you should cast the whole object to some class containing those properties:
var data = list.Where(i => i.Id == 1).Cast<Pet>().Distinct().ToList();
I would like a method to return a Dictionary of Dictionary from an IQueryable formula. Typically,
Dictionary<int, Dictionary<DateTime, string>>
At first, I looked at ToDictionary() of course but couldn't find a way to declare two one after the other.
Then, i loooked at ToLookup() and I have a way to carry this with a ILookup with the string being my secondary dictionary(the DateTime.Tostring() + the other string)... you follow ? :)
But I don't find the ILookup solution really confortable (parsing a date to string and when i receive the data -> string to date.. beuark)
That would give something like that
return this.ttdc.Trackers.GroupBy(t => new { TrackerTaskId = t.TrackerTaskID, DateGenerated = t.TrackerDateGenerated })
.Select(t => new { taskId = t.Key.TrackerTaskId, DateGenerated = t.Key.DateGenerated, count = t.Count() })
.ToLookup(k => k.taskId.Value, k => k.DateGenerated.Value.ToString() + "|" + k.count);
Now, i'm thinking about creating a List of self created class with the 3 informations I need as properties.
Could you help me chosing the best pattern ? This method is hosted on a windows service and I would like to limit the amount of data transfered to the client.
Thank you
Here's an example, using GroupBy and ToDictionary:
var testValues = new[]{new {ID = 1, Date = new DateTime(2010,1,1), Str = "Val1"},
new {ID = 1, Date = new DateTime(2011,2,2), Str = "Val2"},
new {ID = 2, Date = new DateTime(2010,1,1), Str = "Val3"}};
var dict = testValues.GroupBy(item => item.ID)
.ToDictionary(grp => grp.Key,
grp => grp.ToDictionary(item => item.Date,
item => item.Str));
I currently have 3 tables:
News
ID | Title
Tag
ID | Name
TaggedContent
ContentID | TagID
And I have two context objects: NewsEntities and TagsEntities
I want to select all tags used by News, in my application I have:
static void Main(string[] args)
{
IEnumerable<dynamic> something = null;
IEnumerable<News.Data.News> news = null;
IEnumerable<Tags.Data.Tag> tags = null;
IEnumerable<TaggedContent> tagged = null;
using (var db = new NewsEntities())
{
news = db.News.ToList(); // 1 select
}
using (var db = new TagsEntities())
{
something = news.Join(db.TaggedContents.ToList(),
n => n.ID,
tc => tc.ContentID,
(n, tc) => new { tc.TagID }); // 1 select
something = something.Join(db.Tags.ToList(),
tid => tid.TagID,
t => t.ID,
(tid, t) => t); // 1 select
}
var result = something;
}
I am currently generating 3 selects. How can I reduce it to 2? Or if possible I would like to reduce to 1 without merging the entities.
Since you're joining entities from different contexts, you can't get away with fewer than 2 selects.
Your join is a simple identity check, so you could do this:
var ids = db.News.Select(x => x.ID).ToList();
You now have a local copy of all of the IDs in the news table -- the first select. Then change your first 'something' to:
something = db.TaggedContents
.Where(x => ids.Contains(x.ContentID))
.Select(x => new { x.TagID });
This second statement won't in itself generate a select because of deferred execution. You can now remove the ToList() in the third statement:
something = something.Join(db.Tags,
tid => tid.TagID,
t => t.ID,
(tid, t) => t);
And when you finally enumerate over something, you'll have your second select.
Using DataLoadOptions class passed in the creation of your DataContext.
DataLoadOptions options = new DataLoadOptions();
db.LoadOptions = options;
options.LoadWith((News n) => n.Tags);
So do for the remaining of the data to join.