Разбить ICollection <T>с помощью последовательности разделителя - PullRequest
4 голосов
/ 20 июня 2011

Это для C # 3.5

У меня есть ICollection, которую я пытаюсь разбить на отдельные ICollections, где разделитель - это последовательность.

Например

ICollection<byte> input = new byte[] { 234, 12, 12, 23, 11, 32, 23, 11 123, 32 };
ICollection<byte> delimiter = new byte[] {23, 11};
List<IICollection<byte>> result = input.splitBy(delimiter);

приведет к

result.item(0) = {234, 12, 12};
result.item(1) = {32};
result.item(2) = {123, 32};

Ответы [ 5 ]

4 голосов
/ 20 июня 2011
private static IEnumerable<IEnumerable<T>> Split<T>
    (IEnumerable<T> source, ICollection<T> delimiter)
{
    // window represents the last [delimeter length] elements in the sequence,
    // buffer is the elements waiting to be output when delimiter is hit

    var window = new Queue<T>();
    var buffer = new List<T>();

    foreach (T element in source)
    {
        buffer.Add(element);
        window.Enqueue(element);
        if (window.Count > delimiter.Count)
            window.Dequeue();

        if (window.SequenceEqual(delimiter))
        {
            // number of non-delimiter elements in the buffer
            int nElements = buffer.Count - window.Count;
            if (nElements > 0)
                yield return buffer.Take(nElements).ToArray();

            window.Clear();
            buffer.Clear();
        }
    }

    if (buffer.Any())
        yield return buffer;
}
2 голосов
/ 21 июня 2011

Оптимальным решением было бы не использовать SequenceEqual() для проверки каждого поддиапазона, в противном случае вы могли бы потенциально использовать итерацию длины разделителя для каждого элемента в последовательности, что может снизить производительность, особенно для последовательностей с большим разделителем.Это может быть проверено, так как вместо этого перечисляется исходная последовательность.

Вот что я написал бы, но всегда есть возможности для улучшения.Я стремился иметь семантику, подобную String.Split().

public enum SequenceSplitOptions { None, RemoveEmptyEntries }
public static IEnumerable<IList<T>> SequenceSplit<T>(
    this IEnumerable<T> source,
    IEnumerable<T> separator)
{
    return SequenceSplit(source, separator, SequenceSplitOptions.None);
}
public static IEnumerable<IList<T>> SequenceSplit<T>(
    this IEnumerable<T> source,
    IEnumerable<T> separator,
    SequenceSplitOptions options)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (options != SequenceSplitOptions.None
     && options != SequenceSplitOptions.RemoveEmptyEntries)
        throw new ArgumentException("Illegal option: " + (int)option);
    if (separator == null)
    {
        yield return source.ToList();
        yield break;
    }

    var sep = separator as IList<T> ?? separator.ToList();
    if (sep.Count == 0)
    {
        yield return source.ToList();
        yield break;
    }

    var buffer = new List<T>();
    var candidate = new List<T>(sep.Count);
    var sindex = 0;
    foreach (var item in source)
    {
        candidate.Add(item);
        if (!item.Equals(sep[sindex]))
        {   // item is not part of the delimiter
            buffer.AddRange(candidate);
            candidate.Clear();
            sindex = 0;
        }
        else if (++sindex >= sep.Count)
        {   // candidate is the delimiter
            if (options == SequenceSplitOptions.None || buffer.Count > 0)
                yield return buffer.ToList();
            buffer.Clear();
            candidate.Clear();
            sindex = 0;
        }
    }
    if (candidate.Count > 0)
        buffer.AddRange(candidate);
    if (options == SequenceSplitOptions.None || buffer.Count > 0)
        yield return buffer;
}
1 голос
/ 20 июня 2011
public IEnumerable<IEnumerable<T>> SplitByCollection<T>(IEnumerable<T> source, 
                                                        IEnumerable<T> delimiter)
{
    var sourceArray = source.ToArray();
    var delimiterCount = delimiter.Count();

    int lastIndex = 0;

    for (int i = 0; i < sourceArray.Length; i++)
    {
        if (delimiter.SequenceEqual(sourceArray.Skip(i).Take(delimiterCount)))
        {
            yield return sourceArray.Skip(lastIndex).Take(i - lastIndex);

            i += delimiterCount;
            lastIndex = i;
        }
    }

    if (lastIndex < sourceArray.Length)
        yield return sourceArray.Skip(lastIndex);
}

Называя это ...

var result = SplitByCollection(input, delimiter);

foreach (var element in result)
{
    Console.WriteLine (string.Join(", ", element));
}

возвращает

234, 12, 12
32
123, 32
0 голосов
/ 20 июня 2011

Вот мое мнение:

public static IEnumerable<IList<byte>> Split(IEnumerable<byte> input, IEnumerable<byte> delimiter)
{
    var l = new List<byte>();
    var set = new HashSet<byte>(delimiter);
    foreach (var item in input)
    {
        if(!set.Contains(item))
            l.Add(item);
        else if(l.Count > 0)
        {
            yield return l;
            l = new List<byte>();
        }
    }
    if(l.Count > 0)
        yield return l;
}
0 голосов
/ 20 июня 2011

Вероятно, есть лучшие методы, но вот один, который я использовал раньше: это хорошо для относительно небольших коллекций:

byte startDelimit = 23;
byte endDelimit = 11;
List<ICollection<byte>> result = new List<ICollection<byte>>();
int lastMatchingPosition = 0;
var inputAsList = input.ToList();

for(int i = 0; i <= inputAsList.Count; i++)
{
    if(inputAsList[i] == startDelimit && inputAsList[i + 1] == endDelimit)
    {
        ICollection<byte> temp = new ICollection<byte>();
        for(int j = lastInputPosition; j <= i ; j++)
        {
            temp.Add(inputAsList[j]);
        }
        result.Add(temp);
        lastMatchingPosition = i + 2;
    }
}

В данный момент у меня нет открытой IDE, так что моя не компилируется как есть или может иметь некоторые дыры, которые вам нужно будет подключить. Но это то, с чего я начинаю, когда сталкиваюсь с этой проблемой. Опять же, как я уже говорил, если это для больших коллекций, это будет медленно - так что лучшие решения еще могут существовать.

...