Группировка по диапазонам непрерывных целых - PullRequest
3 голосов
/ 08 февраля 2011

Я проверил другие публикации, включая Группировка по диапазону целочисленных переменных, используя Linq

, но я не нашел ничего похожего на мой вопрос ... Я пытаюсь сгруппировать в целое числодиапазоны, где целочисленная последовательность прерывистая.Например, если у меня есть набор непрерывных целых чисел от 1 до 100, а затем мой набор пропускает 101, я бы хотел создать запись, которая берет дату из записей № 1 и № 100, где дата из записи № 1 являетсядата начала и # 100 - дата окончания.

Каждый диапазон непрерывных целых чисел создает новую запись для добавления в список записей, которые указывают дату в начале и конце диапазона.если диапазон содержит только одно целочисленное значение (например, целочисленные диапазоны идут от 1 до 100, 102 и 104–200), у единственного целочисленного диапазона будет одинаковая дата начала и окончания.

Есть предложения?

Ответы [ 3 ]

7 голосов
/ 08 февраля 2011

Вы можете создать метод расширения, который сделает это:

static class EnumerableIntExtensions {
    public static IEnumerable<IEnumerable<T>> ToContiguousSequences<T>(
        this IEnumerable<T> sequence,
        Func<T, T> next
    ) {
        Contract.Requires(sequence != null);
        Contract.Requires(next != null);
        var e = sequence.GetEnumerator();
        if (!e.MoveNext()) {
            throw new InvalidOperationException("Sequence is empty.");
        }
        var currentList = new List<T> { e.Current };
        while (e.MoveNext()) {
            T current = e.Current;
            if (current.Equals(next(currentList.Last()))) {
                currentList.Add(current);
            }
            else {
                yield return currentList;
                currentList = new List<T> { current };
            }
        }
        yield return currentList;
    }
}

Использование:

var sequence = Enumerable.Range(1, 100)
                         .Concat(new[] { 102 })
                         .Concat(Enumerable.Range(104, 97));
var sequences = sequence.ToContiguousSequences(n => n + 1);
foreach(var contiguousSequence in sequences) {
    Console.WriteLine(String.Join(", ", contiguousSequence.Select(n => n.ToString())));
}

Выход:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100
102
104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200
0 голосов
/ 04 марта 2013

Вот пример класса, который я создал, который может помочь. Он преобразует последовательность целых чисел в набор диапазонов. Код работает в LINQPad.

void Main()
{
    ContiguousNumberLine line = new ContiguousNumberLine();

    List<Int64> testItems = new List<Int64>() { 1, 2, 4, 5, 7, 9, 11, 10, 3, 6, 8, 8 };
    foreach(Int64 item in testItems)
    {
        line.Add(item);
        line.numberLine.Dump(string.Format("After adding {0}", item));
    }

    line.Add(new List<Int64>() { 12, 14, 15, 16 });
    line.numberLine.Dump("After adding 12, 14, 15, 16");
    line.Add(new List<Int64>() { 17, 18, 19, 20 });
    line.numberLine.Dump("After adding 17, 18, 19, 20");

    foreach(var item in line.numberLine)
    {
        Console.WriteLine(string.Format("{0}\t{1}", item.Key, item.Value));
    }
}

// Define other methods and classes here
public class ContiguousNumberLine
{
    public SortedList<Int64, Int64> numberLine { get; private set; }

    public ContiguousNumberLine()
    {
        numberLine = new SortedList<Int64, Int64>();
    }

    public bool IsPresent(Int64 input)
    {
        if (numberLine.ContainsKey(input))
        {
            return true;
        }
        else
        {
            foreach(var numRange in numberLine)
            {
                if (numRange.Key > input)
                {
                    return false;
                }
                if (numRange.Value < input)
                {
                    continue;
                }
                if (numRange.Key <= input && input <= numRange.Value)
                {
                    return true;
                }
            }
            return false;
        }
    }

    public bool IsPresent(List<Int64> input)
    {
        if (IsContiguous(input))
        {
            return IsPresentRange(input.First(), input.Last());
        }
        else
        {
            foreach(Int64 item in input)
            {
                if (this.IsPresent(item))
                {
                    return true;
                }
            }
        }
        return false;
    }

    public bool IsPresentRange(Int64 first, Int64 last)
    {
        // naive implementation
        for(Int64 i = first; i <= last; i++)
        {
            if (this.IsPresent(i))
            {
                return true;
            }
        }
        return false;
    }

    public bool IsContiguous(List<Int64> input)
    {
        Int64? first = null;
        Int64? last = null;
        foreach(Int64 item in input)
        {
            if (first == null)
            {
                first = item;
                last = item;
                continue;
            }
            if (last.Value +1 == item)
            {
                last = item;
                continue;
            }
            else
            {
                return false;
            }
        }
        return true;
    }

    public bool Add(Int64 input)
    {
        if (IsPresent(input))
        {
            return false;
        }
        else
        {
            //numberLine.Add(input, input);
            // extend the last number of an existing range, then check if it bridges the gap to the next range
            KeyValuePair<Int64, Int64>? itemToRemove1 = null;
            KeyValuePair<Int64, Int64>? itemToRemove2 = null;
            KeyValuePair<Int64, Int64> itemToAdd = new KeyValuePair<Int64, Int64>(input, input) ;

            foreach(var numRange in numberLine)
            {
                if (numRange.Value + 1 == input)
                {
                    itemToRemove1 = numRange;
                    itemToAdd = new KeyValuePair<Int64, Int64>(numRange.Key, numRange.Value + 1);
                    continue;
                } else if (itemToRemove1 != null && numRange.Key -1 == input)
                {
                    itemToRemove2 = new KeyValuePair<Int64, Int64>(numRange.Key, numRange.Value);
                    KeyValuePair<Int64, Int64> newRange = new KeyValuePair<Int64, Int64>(itemToAdd.Key, numRange.Value);
                    itemToAdd = newRange;
                    break;
                } else if (numRange.Key - 1 == input)
                {
                    itemToRemove1 = numRange;
                    itemToAdd = new KeyValuePair<Int64, Int64>(input, numRange.Value);
                    break;
                } else if (numRange.Key > input)
                {
                    itemToAdd = new KeyValuePair<Int64, Int64>(input, input);
                    break;
                }
            }

            if (itemToRemove1 != null)
            {
                numberLine.Remove(itemToRemove1.Value.Key);
            }
            if (itemToRemove2 != null)
            {
                numberLine.Remove(itemToRemove2.Value.Key);
            }
            numberLine.Add(itemToAdd.Key, itemToAdd.Value);
            return true;
        }
    }

    public bool Add(List<Int64> input)
    {
        // check if we can use the optimized version
        if (IsContiguous(input))
        {
            return this.AddRange(input.First(), input.Last());
        }
        else
        {
            if(IsPresent(input))
            {
                return false;
            }
            foreach(Int64 item in input)
            {
                this.Add(item);
            }
            return true;
        }

    }

    public bool AddRange(Int64 first, Int64 last)
    {
        //throw new NotImplementedException();
        if(IsPresentRange(first, last))
        {
            return false;
        }
        // naive implementation below (TODO: write optimized version)
        for(Int64 i = first; i <= last; i++)
        {
            this.Add(i);
        }
        return true;
    }

}
0 голосов
/ 08 февраля 2011

Я не знаю, сможете ли вы сделать это, используя предоставленные функции LINQ, но следующую функцию:

public static IEnumerable<IEnumerable<T>> GroupWhile<T>(this IEnumerable<T> values, Func<Int32, Boolean> predicate)
{
    var i = 0;
    var currentSet = new List<T>();
    while (i < values.Count())
    {
        if (predicate(i))
            currentSet.Add(values.Skip(i).First());
        else
        {
            yield return currentSet;
            currentSet = new List<T>(new[] {values.Skip(i).First()});
        }
        i++;
    }
    yield return currentSet;
}

используется следующим образом:

var ints = new[] {1, 2, 4, 5, 7, 8};
var groups = ints.GroupWhile(i => i == 0 || ints[i - 1] == ints[i] - 1).ToList();

может соответствовать требованиям.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...