HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

List with no sequential repeats (where possible)

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withwhererepeatssequentialpossiblelist

Problem

Assume I have the following list (List)

G

G

M

T

I'd like this to be in an order where the same letter will not occur twice, such as

G

M

G

T

(I appreciate other variations would also suffice)

Please note, if I had the following

G

G

G

G

G

M

T

Then of course some values will have to occur concurrently. The position within the List where this concurrency occurs is not relevant. What I'm after is to stop this repetition where possible to most evenly spread the letters.

The C# console application code I share works

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace resorting
{
    class Program
    {
        static void Main(string[] args)
        {
            List list = new List { "G", "G", "G", "G", "M", "M", "T", "T", "T", "T", "T", "T", "T", "T", "G", "G", "G", "G", "M", "M", "T", "T", "T", "T", "T", "T", "T", "T" };

            list.UpdateList();
        }      
    }

    public static class extension
    {
        public static void UpdateList(this List list)
        {
            GetList(list);
            list.Reverse(); // DO I HAVE TO REVERSE
            GetList(list);  // AND THEN CALL THE METHOD AGAIN
        }

        private static void GetList(List list)
        {
            int total = list.Count;

            int inc = 2;

            for (int i = 0; i < total; i++)
            {
                if (total <= i + 2)
                    break;

                if (total <= i + inc)
                    continue;

                if (list[i] == list[i + 1])
                {
                    var val = list[i];
                    list.RemoveAt(i);
                    list.Insert(i + inc, val);
                    i = i - 1;
                    inc++;
                    continue;
                }
                inc = 2;
            }
        }
    }
}


As you can see, I have to call this function twice. I reverse the order and then reapply the same logic. Whilst it work

Solution

Here's an alternate way to do it. It doesn't involve modifying and reversing the existing list. Instead, it first groups and counts the items, then yields the one that wasn't printed last time and still needs to be yielded the most times. I'm not sure about actual runtime for large lists, but as long as the number of distinct items in the list is small, the reorderings and searches should run very fast.

public static IEnumerable WithoutDupes(this IEnumerable source) where T : IEquatable
{
    var sourceGroups = source.GroupBy(x => x).Select(x => new GroupCount(x)).ToList();
    T last = default(T);
    bool hasLast = false;
    while (sourceGroups.Count > 0)
    {
        sourceGroups = sourceGroups.OrderByDescending(x => x.Count - x.CountOutputted).ToList();
        var group = (hasLast ? sourceGroups.FirstOrDefault(x => !last.Equals(x.Value)) : null) ?? sourceGroups[0];

        yield return group.Value;
        group.CountOutputted++;
        if (group.CountOutputted == group.Count)
        {
            sourceGroups.Remove(group);
        }
        last = group.Value;
        hasLast = true;
    }
}
private class GroupCount
{
    public T Value { get; private set; }
    public int Count { get; private set; }
    public int CountOutputted { get; set; }
    public GroupCount(IGrouping grouping)
    {
        this.Value = grouping.Key;
        this.Count = grouping.Count();
    }
}


As example output, with your input, it produces this list (note how it handles the impossible case: puts the extra T's at the end)

T, G, T, G, T, G, T, G, T, G, T, M, T, M, T, G, T, G, T, M, T, M, T, G, T, T, T, T

Code Snippets

public static IEnumerable<T> WithoutDupes<T>(this IEnumerable<T> source) where T : IEquatable<T>
{
    var sourceGroups = source.GroupBy(x => x).Select(x => new GroupCount<T>(x)).ToList();
    T last = default(T);
    bool hasLast = false;
    while (sourceGroups.Count > 0)
    {
        sourceGroups = sourceGroups.OrderByDescending(x => x.Count - x.CountOutputted).ToList();
        var group = (hasLast ? sourceGroups.FirstOrDefault(x => !last.Equals(x.Value)) : null) ?? sourceGroups[0];

        yield return group.Value;
        group.CountOutputted++;
        if (group.CountOutputted == group.Count)
        {
            sourceGroups.Remove(group);
        }
        last = group.Value;
        hasLast = true;
    }
}
private class GroupCount<T>
{
    public T Value { get; private set; }
    public int Count { get; private set; }
    public int CountOutputted { get; set; }
    public GroupCount(IGrouping<T, T> grouping)
    {
        this.Value = grouping.Key;
        this.Count = grouping.Count();
    }
}
T, G, T, G, T, G, T, G, T, G, T, M, T, M, T, G, T, G, T, M, T, M, T, G, T, T, T, T

Context

StackExchange Code Review Q#49225, answer score: 10

Revisions (0)

No revisions yet.