Skip to content

Commit 20689be

Browse files
committed
Add initial implementation of BindByIndex
1 parent 9f6dc48 commit 20689be

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed

MoreLinq/BindByIndex.cs

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#region License and Terms
2+
// MoreLINQ - Extensions to LINQ to Objects
3+
// Copyright (c) 2018 Atif Aziz. All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
#endregion
17+
18+
namespace MoreLinq
19+
{
20+
using System;
21+
using System.Collections;
22+
using System.Collections.Generic;
23+
24+
static partial class MoreEnumerable
25+
{
26+
/// <summary>
27+
/// TODO Complete documentation
28+
/// </summary>
29+
/// <typeparam name="T">
30+
/// Type of elements in <paramref name="source"/> sequence.</typeparam>
31+
/// <typeparam name="TResult">Type of result elements returned.</typeparam>
32+
/// <param name="source">The source sequence.</param>
33+
/// <param name="indicies">The sequence of indicies.</param>
34+
/// <param name="missingSelector">
35+
/// TODO Complete documentation
36+
/// </param>
37+
/// <param name="resultSelector">
38+
/// TODO Complete documentation
39+
/// </param>
40+
/// <returns>
41+
/// TODO Complete documentation
42+
/// </returns>
43+
44+
public static IEnumerable<TResult>
45+
BindByIndex<T, TResult>(this IEnumerable<T> source, IEnumerable<int> indicies,
46+
Func<int, TResult> missingSelector, Func<T, int, TResult> resultSelector) =>
47+
BindByIndex(source, indicies, null, missingSelector, resultSelector);
48+
49+
/// <summary>
50+
/// TODO Complete documentation
51+
/// </summary>
52+
/// <typeparam name="T">
53+
/// Type of elements in <paramref name="source"/> sequence.</typeparam>
54+
/// <typeparam name="TResult">Type of result elements returned.</typeparam>
55+
/// <param name="source">The source sequence.</param>
56+
/// <param name="indicies">The sequence of indicies.</param>
57+
/// <param name="lookBackSize">Size of look-back buffer.</param>
58+
/// <param name="missingSelector">
59+
/// TODO Complete documentation
60+
/// </param>
61+
/// <param name="resultSelector">
62+
/// TODO Complete documentation
63+
/// </param>
64+
/// <returns>
65+
/// TODO Complete documentation
66+
/// </returns>
67+
68+
public static IEnumerable<TResult>
69+
BindByIndex<T, TResult>(this IEnumerable<T> source, IEnumerable<int> indicies,
70+
int lookBackSize,
71+
Func<int, TResult> missingSelector,
72+
Func<T, int, TResult> resultSelector) =>
73+
BindByIndex(source, indicies, (int?) lookBackSize, missingSelector, resultSelector);
74+
75+
static IEnumerable<TResult>
76+
BindByIndex<T, TResult>(IEnumerable<T> source, IEnumerable<int> indicies,
77+
int? lookBackSize,
78+
Func<int, TResult> missingSelector,
79+
Func<T, int, TResult> resultSelector)
80+
{
81+
if (source == null) throw new ArgumentNullException(nameof(source));
82+
if (indicies == null) throw new ArgumentNullException(nameof(indicies));
83+
if (lookBackSize < 0) throw new ArgumentOutOfRangeException(nameof(lookBackSize));
84+
if (missingSelector == null) throw new ArgumentNullException(nameof(missingSelector));
85+
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));
86+
87+
// TODO A version optimized for lists
88+
89+
return _(lookBackSize is int lbs ? lbs > 0 ? new Queue<T>(lbs, lbs) : null
90+
: new Queue<T>());
91+
92+
IEnumerable<TResult> _(Queue<T> queue)
93+
{
94+
using (var rie = indicies.GetEnumerator())
95+
{
96+
if (!rie.MoveNext())
97+
yield break;
98+
99+
while (rie.Current < 0)
100+
{
101+
yield return missingSelector(rie.Current);
102+
if (!rie.MoveNext())
103+
yield break;
104+
}
105+
106+
var ri = rie.Current;
107+
var si = 0;
108+
109+
foreach (var item in source)
110+
{
111+
while (si == ri)
112+
{
113+
yield return resultSelector(item, si);
114+
do
115+
{
116+
if (!rie.MoveNext())
117+
yield break;
118+
ri = rie.Current;
119+
if (ri < si)
120+
{
121+
if (si - queue?.Count is int qi && ri >= qi)
122+
yield return resultSelector(queue[ri - qi], ri);
123+
else
124+
yield return missingSelector(ri);
125+
}
126+
}
127+
while (ri < si);
128+
}
129+
130+
queue?.Enqueue(item);
131+
si++;
132+
}
133+
134+
if (ri != si)
135+
{
136+
yield return missingSelector(ri);
137+
while (rie.MoveNext())
138+
yield return missingSelector(rie.Current);
139+
}
140+
}
141+
}
142+
}
143+
144+
/// <summary>
145+
/// A queue implementation similar to
146+
/// <see cref="System.Collections.Generic.Queue{T}"/> but which
147+
/// supports a maximum count (exceeding which will cause an item to be
148+
/// dequeued each to make space for a new one being queued) as well as
149+
/// directly indexing into the queue to retrieve any one item.
150+
/// </summary>
151+
152+
sealed class Queue<T> : IReadOnlyList<T>
153+
{
154+
T[] _items;
155+
int _firstIndex;
156+
readonly int _maxCount;
157+
158+
static readonly T[] ZeroItems = new T[0];
159+
160+
public Queue(int maxCount = 0, int capacity = 0)
161+
{
162+
_items = capacity > 0 ? new T[capacity] : ZeroItems;
163+
_firstIndex = 0;
164+
_maxCount = maxCount;
165+
Count = 0;
166+
}
167+
168+
int Capacity => _items.Length;
169+
public int Count { get; private set; }
170+
171+
T IReadOnlyList<T>.this[int index] => this[index];
172+
173+
public T this[int index]
174+
{
175+
get
176+
{
177+
if (index < 0 || index >= Count)
178+
throw new IndexOutOfRangeException();
179+
return Cell(index);
180+
}
181+
}
182+
183+
ref T Cell(int index) => ref _items[(_firstIndex + index) % Capacity];
184+
185+
public void Enqueue(T item)
186+
{
187+
if (_maxCount > 0 && Count == _maxCount)
188+
Dequeue();
189+
190+
if (Count == Capacity)
191+
{
192+
var array = new T[Math.Max(4, Capacity * 2)];
193+
for (var i = 0; i < Count; i++)
194+
array[i] = this[i];
195+
_firstIndex = 0;
196+
_items = array;
197+
}
198+
199+
Cell(Count++) = item;
200+
}
201+
202+
public T Dequeue()
203+
{
204+
if (Count == 0)
205+
throw new InvalidOperationException();
206+
var result = this[0];
207+
_firstIndex++;
208+
--Count;
209+
return result;
210+
}
211+
212+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
213+
214+
public IEnumerator<T> GetEnumerator()
215+
{
216+
for (var i = 0; i < Count; i++)
217+
yield return this[i];
218+
}
219+
}
220+
}
221+
}

0 commit comments

Comments
 (0)