1

So I have a list of DefaultSearchModal items that have an enum called TypeSearch in every list item. I would like to order the list to have a different enum property each row.

So if you have an unordered list by this:

1,1,2,2,3,3,4,4,5,5,5,5,5,5

I would like to order it to:

1,2,3,4,5,1,2,3,4,5,5,5,5,5

What would be a good algorithm to sort it like this?

    public class DefaultSearchModel
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string Url { get; set; }
    public TypeSearch TypeSearch { get; set; }
}

public enum TypeSearch
        {
           News = 0,
           Blog = 1,
           Photo = 3,
           Page = 4
        }
5
  • 1
    You've provided plenty of information for someone to come up with a solution, but you should also make an attempt at it yourself and share the code.
    – JeffO
    Commented Nov 5, 2014 at 20:09
  • You just want to order by TypeSearch, but can't have consecutive enums if possible? I guess I'm not sure what the significance of your specific order means, that could help in defining a way to make it happen.
    – bowlturner
    Commented Nov 5, 2014 at 21:16
  • @bowlturner This is ment for a website search. So i need to display different found items. I want to avoid that you first see 5 pages of news articles and then the other found stuff.
    – Jamie
    Commented Nov 5, 2014 at 21:31
  • @Jamie: What have you tried so far? Any code to show, even if it doesn't quite work the way you want? Commented Nov 5, 2014 at 21:33
  • OK so there is no connection between 1,2,3,4,5 just take the first of each available then the next etc?
    – bowlturner
    Commented Nov 5, 2014 at 21:34

4 Answers 4

1

Personally I would just put it in a loop Here's psudo code for it.

while mainList has items
{
  find first 0 item.
  add to queue, 
  remove from mailList

  find first 1 item 
  add to queue, 
  remove from mailList

  find first 3 item.
  add to queue, 
  remove from mailList

  find first 4 item.
  add to queue, 
  remove from mailList
}

return queue
2
  • Great idea to remove the found item from the list and keep going till there are no more items left. I was way overthinking this. Thanks!
    – Jamie
    Commented Nov 5, 2014 at 21:51
  • Glad to be of service!
    – bowlturner
    Commented Nov 5, 2014 at 21:58
0

The end result thanks to bowlturner:

  private IEnumerable<DefaultSearchModel> Sort(IList<DefaultSearchModel> items)
    {
        var returnList = new Collection<DefaultSearchModel>();

        while (items.Any())
        {
            foreach (var type in Enum.GetValues(typeof (TypeSearch)))
            {
                if (items.Any(x => x.TypeSearch == (TypeSearch) type))
                {
                    var item = items.FirstOrDefault(x => x.TypeSearch == (TypeSearch) type);
                    returnList.Add(item);
                    items.Remove(item);
                }
            }
        }

        return returnList;
    }
1
  • This is a bit inefficient. Eg. after .Any(x => x.TypeSearch == (TypeSearch) type) you're calling .FirstOrDefault(x => x.TypeSearch == (TypeSearch) type), which searches for the same matching item once again. And why OrDefault (instead of simply First), given that you've only just confirmed there must be at least one item that matches the criteria?? You could have skipped Any, use FirstOrDefault only and just give up if it returns null. Commented Nov 5, 2014 at 22:00
0

My FP-flavored solution:

IEnumerable<DefaultSearchModel> DoMagic(IEnumerable<DefaultSearchModel> models)
{
    var groups = models.GroupBy(model => model.TypeSearch);
    var solution = Enumerable
        .Range(0, groups.Max(grp => grp.Count()))
        .SelectMany(i => groups
            .Where(grp => grp.Count() > i)
            .Select(grp => grp.ElementAt(i))
            .OrderBy(model => model.TypeSearch));
    return solution;
}

Probably not most efficient either (since I pointed out that @Jamie's solution isn't, I can't be a hypocrite), but it is not my point right now. I just wanted to show how terse and non-imperative LINQ can be.

Note that we are not repeatedly looking for TypeSearch values that aren't on the list at all, or whose count is beyond a specific treshold.

2
  • Not the most readable either... :-)
    – fishinear
    Commented Nov 6, 2014 at 14:48
  • @fishinear surely it takes a while to learn to parse it. But once you get used to the paradigm, it isn't so difficult. I find SQL unreadable every time I haven't had worked with raw SQL for a while :) Note that you can always break a complex expression down into a few helpfully named local variables. One significant advantage of using, say, ready-made GroupBy instead of implementing it by yourself "manually" is that GroupBy has already been tested a zilion times and it's very unlikely that there are any bugs in it. The same cannot be said about our implementation, custom-written ad hoc. Commented Nov 6, 2014 at 15:15
0
  1. Go once through the list and divide them up in 5 sets.
  2. Then simply take one object (if available) from each set in order, and put them into your output list
  3. Repeat step 2 until all sets are empty

Algorithm has O(n)

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.