Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MoreLinq.Test/WindowLeftTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void WindowLeftWithSingleElement()
const int count = 100;
var sequence = Enumerable.Range(1, count).ToArray();

IList<int>[] result;
IReadOnlyList<int>[] result;
using (var ts = sequence.AsTestingSequence())
result = ts.WindowLeft(1).ToArray();

Expand Down
2 changes: 1 addition & 1 deletion MoreLinq.Test/WindowRightTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void WindowRightWithSingleElement()
const int count = 100;
var sequence = Enumerable.Range(1, count).ToArray();

IList<int>[] result;
IReadOnlyList<int>[] result;
using (var ts = sequence.AsTestingSequence())
result = ts.WindowRight(1).ToArray();

Expand Down
6 changes: 3 additions & 3 deletions MoreLinq/Extensions.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6661,7 +6661,7 @@ public static partial class WindowExtension
/// <param name="size">The size (number of elements) in each window</param>
/// <returns>A series of sequences representing each sliding window subsequence</returns>

public static IEnumerable<IList<TSource>> Window<TSource>(this IEnumerable<TSource> source, int size)
public static IEnumerable<IReadOnlyList<TSource>> Window<TSource>(this IEnumerable<TSource> source, int size)
=> MoreEnumerable.Window(source, size);

}
Expand Down Expand Up @@ -6706,7 +6706,7 @@ public static partial class WindowLeftExtension
/// ]]></code>
/// </example>

public static IEnumerable<IList<TSource>> WindowLeft<TSource>(this IEnumerable<TSource> source, int size)
public static IEnumerable<IReadOnlyList<TSource>> WindowLeft<TSource>(this IEnumerable<TSource> source, int size)
=> MoreEnumerable.WindowLeft(source, size);

}
Expand Down Expand Up @@ -6751,7 +6751,7 @@ public static partial class WindowRightExtension
/// ]]></code>
/// </example>

public static IEnumerable<IList<TSource>> WindowRight<TSource>(this IEnumerable<TSource> source, int size)
public static IEnumerable<IReadOnlyList<TSource>> WindowRight<TSource>(this IEnumerable<TSource> source, int size)
=> MoreEnumerable.WindowRight(source, size);

}
Expand Down
39 changes: 16 additions & 23 deletions MoreLinq/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,31 @@ public static partial class MoreEnumerable
/// <param name="size">The size (number of elements) in each window</param>
/// <returns>A series of sequences representing each sliding window subsequence</returns>

public static IEnumerable<IList<TSource>> Window<TSource>(this IEnumerable<TSource> source, int size)
public static IEnumerable<IReadOnlyList<TSource>> Window<TSource>(this IEnumerable<TSource> source, int size)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size));

return _(); IEnumerable<IList<TSource>> _()
return _(); IEnumerable<IReadOnlyList<TSource>> _()
{
using var iter = source.GetEnumerator();
var cache = new List<TSource>();

// generate the first window of items
var window = new TSource[size];
int i;
for (i = 0; i < size && iter.MoveNext(); i++)
window[i] = iter.Current;
using var enumerator = source.GetEnumerator();

if (i < size)
yield break;
var hasNext = true;
bool MoveNext() => hasNext && (hasNext = enumerator.MoveNext());

// return the first window (whatever size it may be)
yield return window;
for (var i = 0; i < size - 1 && MoveNext(); i++)
{
cache.Add(enumerator.Current);
}

// generate the next window by shifting forward by one item
while (iter.MoveNext())
var offset = 0;
while (MoveNext())
{
// NOTE: If we used a circular queue rather than a list,
// we could make this quite a bit more efficient.
// Sadly the BCL does not offer such a collection.
var newWindow = new TSource[size];
Array.Copy(window, 1, newWindow, 0, size - 1);
newWindow[size - 1] = iter.Current;
yield return newWindow;
window = newWindow;
cache.Add(enumerator.Current);
yield return new WindowedList<TSource>(cache, offset, cache.Count - offset);
offset++;
}
}
}
Expand Down
37 changes: 24 additions & 13 deletions MoreLinq/WindowLeft.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,37 @@ public static partial class MoreEnumerable
/// ]]></code>
/// </example>

public static IEnumerable<IList<TSource>> WindowLeft<TSource>(this IEnumerable<TSource> source, int size)
public static IEnumerable<IReadOnlyList<TSource>> WindowLeft<TSource>(this IEnumerable<TSource> source, int size)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size));

return _(); IEnumerable<IList<TSource>> _()
return _(); IEnumerable<IReadOnlyList<TSource>> _()
{
var window = new List<TSource>();
foreach (var item in source)
var cache = new List<TSource>();

using var enumerator = source.GetEnumerator();

var hasNext = true;
bool MoveNext() => hasNext && (hasNext = enumerator.MoveNext());

for (var i = 0; i < size && MoveNext(); i++)
{
window.Add(item);
if (window.Count < size)
continue;
yield return window;
window = new List<TSource>(window.Skip(1));
cache.Add(enumerator.Current);
}
while (window.Count > 0)

var offset = 0;
while (MoveNext())
{
yield return new WindowedList<TSource>(cache, offset, cache.Count - offset);
cache.Add(enumerator.Current);
offset++;
}

while (offset < cache.Count)
{
yield return window;
window = new List<TSource>(window.Skip(1));
yield return new WindowedList<TSource>(cache, offset, cache.Count - offset);
offset++;
}
}
}
Expand Down
29 changes: 7 additions & 22 deletions MoreLinq/WindowRight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,34 +58,19 @@ public static partial class MoreEnumerable
/// ]]></code>
/// </example>

public static IEnumerable<IList<TSource>> WindowRight<TSource>(this IEnumerable<TSource> source, int size)
public static IEnumerable<IReadOnlyList<TSource>> WindowRight<TSource>(this IEnumerable<TSource> source, int size)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size));

return source.WindowRightWhile((_, i) => i < size);
}

/// <summary>
/// Creates a right-aligned sliding window over the source sequence
/// with a predicate function determining the window range.
/// </summary>

static IEnumerable<IList<TSource>> WindowRightWhile<TSource>(
this IEnumerable<TSource> source,
Func<TSource, int, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));

return _(); IEnumerable<IList<TSource>> _()
return _(); IEnumerable<IReadOnlyList<TSource>> _()
{
var window = new List<TSource>();
foreach (var item in source)
var cache = new List<TSource>();
var offset = -size;
foreach (var element in source)
{
window.Add(item);
yield return window;
window = new List<TSource>(predicate(item, window.Count) ? window : window.Skip(1));
cache.Add(element);
yield return new WindowedList<TSource>(cache, Math.Max(0, ++offset), Math.Min(size, cache.Count));
}
}
}
Expand Down
58 changes: 58 additions & 0 deletions MoreLinq/WindowedList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#region License and Terms
// MoreLINQ - Extensions to LINQ to Objects
// Copyright (c) 2008 Jonathan Skeet. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion

namespace MoreLinq
{
using System;
using System.Collections;
using System.Collections.Generic;

internal class WindowedList<T> : IReadOnlyList<T>
{
private readonly IReadOnlyList<T> _source;
private readonly int _offset;

public WindowedList(IReadOnlyList<T> source, int offset, int size)
{
_source = source;
_offset = offset;
Count = size;
}

public T this[int index]
{
get
{
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException(nameof(index));
return _source[index + _offset];
}
}

public int Count { get; }

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

public IEnumerator<T> GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return this[i];
}
}
}
}