Reputation: 57
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.
Upvotes: 2
Views: 230
Reputation: 57
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();
Upvotes: 1
Reputation: 6791
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.
Upvotes: 2
Reputation: 989
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);
Upvotes: 1