patterncsharpModerate
List with no sequential repeats (where possible)
Viewed 0 times
withwhererepeatssequentialpossiblelist
Problem
Assume I have the following 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
As you can see, I have to call this function twice. I reverse the order and then reapply the same logic. Whilst it work
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.
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)
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, TCode 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, TContext
StackExchange Code Review Q#49225, answer score: 10
Revisions (0)
No revisions yet.