2

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.

4
  • How is what you want to get any different than you users array? Commented May 25, 2022 at 13:37
  • 1
    It looks like you have the "Pinned" property set at User class creation. isnt it better to insert pinned users at the head of a list (use Insert method) and unpinned at the back (use Add method)? Commented May 25, 2022 at 13:39
  • @Cleptus It's different because I will always get the users array ordered by user ids and I don't want to display it as such. Commented May 26, 2022 at 5:28
  • @MCLinkTimeError that is one option but I was unable to find a solution to order an array by the order of a certain property in another array, if it makes sense. Commented May 26, 2022 at 5:30

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

7 Comments

This does exactly what I want it and it does a decent job even with bigger arrays so thank you very much for the help. I just couldn't wrap my head around this problem but I see what you did now.
I think this works only because the 3 and 1 in the { new PinnedItem(3), new PinnedItem(1) } match what OP wants but it does not mantain the order the items were added to the var pinnedItems. To avoid that bug the PinnedItem class should have a DisplayOrder property and the ThenByDescending should use that new property.
@Cleptus true but that is honestly not a problem because the rest of the array is ordered alphabetically anyway(at some point), I was just struggling with the part where I had to order only certain users by not an array of integers but an array of classes where the property is an integer. I'm struggling to put it in words but I hope it's clear what I mean. Like David said, I later tuned the sorting so I got the result I wanted
@DavidOsborne it falls apart when it's ordered by userIds descending as it orders all pinned users by userId, so let's say every user is pinned but the order of user ids should be 3, 1, 2. The code above will always return users in order 3, 2, 1. Maybe I am doing something wrong as I'm fairly new to programming but I couldn't make the group join work as I needed it to so I will (when I have time) try both codes from this answer and the one I posted to see if they return the same result if given same starting arrays.
@DavidOsborne I really tried, but I can't make the group join work. It might just be that I don't know how to use it properly yet. Whatever the case may be I am still very grateful that you took the time to try to help me
|
1

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);

3 Comments

or just use the users array ordered by the Pinned property
That was my first thought aswell, but I was unable to make a quick example for that. Could you ellaborate?
This solution works, it's very suboptimal as far as performance goes but it does do it's job so thank you.
1

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();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.