Skip to content

Commit 9ad18ad

Browse files
author
azizatif
committed
OrderedMerge operator for merging two heterogeneous sequences ordered by a common key into a homogeneous one
1 parent 3fb4ee4 commit 9ad18ad

File tree

2 files changed

+268
-0
lines changed

2 files changed

+268
-0
lines changed

MoreLinq/MoreLinq.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@
110110
<Compile Include="Batch.cs">
111111
<DependentUpon>MoreEnumerable.cs</DependentUpon>
112112
</Compile>
113+
<Compile Include="OrderedMerge.cs">
114+
<DependentUpon>MoreEnumerable.cs</DependentUpon>
115+
</Compile>
113116
<Compile Include="Random.cs">
114117
<DependentUpon>MoreEnumerable.cs</DependentUpon>
115118
</Compile>

MoreLinq/OrderedMerge.cs

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#region License and Terms
2+
// MoreLINQ - Extensions to LINQ to Objects
3+
// Copyright (c) 2008 Jonathan Skeet. 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+
#region Imports
21+
22+
using System;
23+
using System.Collections.Generic;
24+
using System.Diagnostics;
25+
26+
#endregion
27+
28+
public static partial class MoreEnumerable
29+
{
30+
/// <summary>
31+
/// Merges two ordered sequences into one. Where the elements equal
32+
/// in both sequences, the element from the first sequence is
33+
/// returned in the resulting sequence.
34+
/// </summary>
35+
/// <remarks>
36+
/// This method uses deferred execution. The behavior is undefined
37+
/// if the sequences are unordered as inputs.
38+
/// </remarks>
39+
40+
public static IEnumerable<T> OrderedMerge<T>(
41+
this IEnumerable<T> first,
42+
IEnumerable<T> second)
43+
{
44+
return OrderedMerge(first, second, null);
45+
}
46+
47+
/// <summary>
48+
/// Merges two ordered sequences into one with an additional
49+
/// parameter specifying how to compare the elements of the
50+
/// sequences. Where the elements equal in both sequences, the
51+
/// element from the first sequence is returned in the resulting
52+
/// sequence.
53+
/// </summary>
54+
/// <remarks>
55+
/// This method uses deferred execution. The behavior is undefined
56+
/// if the sequences are unordered as inputs.
57+
/// </remarks>
58+
59+
public static IEnumerable<T> OrderedMerge<T>(
60+
this IEnumerable<T> first,
61+
IEnumerable<T> second,
62+
IComparer<T> comparer)
63+
{
64+
return OrderedMerge(first, second, e => e, f => f, s => s, (a, _) => a, comparer);
65+
}
66+
67+
/// <summary>
68+
/// Merges two ordered sequences into one with an additional
69+
/// parameter specifying the element key by which the sequences are
70+
/// ordered. Where the keys equal in both sequences, the
71+
/// element from the first sequence is returned in the resulting
72+
/// sequence.
73+
/// </summary>
74+
/// <remarks>
75+
/// This method uses deferred execution. The behavior is undefined
76+
/// if the sequences are unordered (by key) as inputs.
77+
/// </remarks>
78+
79+
public static IEnumerable<T> OrderedMerge<T, TKey>(
80+
this IEnumerable<T> first,
81+
IEnumerable<T> second,
82+
Func<T, TKey> keySelector)
83+
{
84+
return OrderedMerge(first, second, keySelector, a => a, b => b, (a, _) => a, null);
85+
}
86+
87+
/// <summary>
88+
/// Merges two ordered sequences into one. Additional parameters
89+
/// specify the element key by which the sequences are ordered,
90+
/// the result when element is found in first sequence but not in
91+
/// the second, the result when element is found in second sequence
92+
/// but not in the first and the result when elements are found in
93+
/// both sequences.
94+
/// </summary>
95+
/// <remarks>
96+
/// This method uses deferred execution. The behavior is undefined
97+
/// if the sequences are unordered (by key) as inputs.
98+
/// </remarks>
99+
100+
public static IEnumerable<TResult> OrderedMerge<T, TKey, TResult>(
101+
this IEnumerable<T> first,
102+
IEnumerable<T> second,
103+
Func<T, TKey> keySelector,
104+
Func<T, TResult> firstSelector,
105+
Func<T, TResult> secondSelector,
106+
Func<T, T, TResult> bothSelector)
107+
{
108+
return OrderedMerge(first, second, keySelector, firstSelector, secondSelector, bothSelector, null);
109+
}
110+
111+
/// <summary>
112+
/// Merges two ordered sequences into one. Additional parameters
113+
/// specify the element key by which the sequences are ordered,
114+
/// the result when element is found in first sequence but not in
115+
/// the second, the result when element is found in second sequence
116+
/// but not in the first, the result when elements are found in
117+
/// both sequences and a method for comparing keys.
118+
/// </summary>
119+
/// <remarks>
120+
/// This method uses deferred execution. The behavior is undefined
121+
/// if the sequences are unordered (by key) as inputs.
122+
/// </remarks>
123+
124+
public static IEnumerable<TResult> OrderedMerge<T, TKey, TResult>(
125+
this IEnumerable<T> first,
126+
IEnumerable<T> second,
127+
Func<T, TKey> keySelector,
128+
Func<T, TResult> firstSelector,
129+
Func<T, TResult> secondSelector,
130+
Func<T, T, TResult> bothSelector,
131+
IComparer<TKey> comparer)
132+
{
133+
return OrderedMerge(first, second, keySelector, keySelector, firstSelector, secondSelector, bothSelector, comparer);
134+
}
135+
136+
/// <summary>
137+
/// Merges two heterogeneous sequences ordered by a common key type
138+
/// into a homogeneous one. Additional parameters specify the
139+
/// element key by which the sequences are ordered, the result when
140+
/// element is found in first sequence but not in the second and
141+
/// the result when element is found in second sequence but not in
142+
/// the first, the result when elements are found in both sequences.
143+
/// </summary>
144+
/// <remarks>
145+
/// This method uses deferred execution. The behavior is undefined
146+
/// if the sequences are unordered (by key) as inputs.
147+
/// </remarks>
148+
149+
public static IEnumerable<TResult> OrderedMerge<TFirst, TSecond, TKey, TResult>(
150+
this IEnumerable<TFirst> first,
151+
IEnumerable<TSecond> second,
152+
Func<TFirst, TKey> firstKeySelector,
153+
Func<TSecond, TKey> secondKeySelector,
154+
Func<TFirst, TResult> firstSelector,
155+
Func<TSecond, TResult> secondSelector,
156+
Func<TFirst, TSecond, TResult> bothSelector)
157+
{
158+
return OrderedMerge(first, second, firstKeySelector, secondKeySelector, firstSelector, secondSelector, bothSelector, null);
159+
}
160+
161+
/// <summary>
162+
/// Merges two heterogeneous sequences ordered by a common key type
163+
/// into a homogeneous one. Additional parameters specify the
164+
/// element key by which the sequences are ordered, the result when
165+
/// element is found in first sequence but not in the second,
166+
/// the result when element is found in second sequence but not in
167+
/// the first, the result when elements are found in both sequences
168+
/// and a method for comparing keys.
169+
/// </summary>
170+
/// <remarks>
171+
/// This method uses deferred execution. The behavior is undefined
172+
/// if the sequences are unordered (by key) as inputs.
173+
/// </remarks>
174+
175+
public static IEnumerable<TResult> OrderedMerge<TFirst, TSecond, TKey, TResult>(
176+
this IEnumerable<TFirst> first,
177+
IEnumerable<TSecond> second,
178+
Func<TFirst, TKey> firstKeySelector,
179+
Func<TSecond, TKey> secondKeySelector,
180+
Func<TFirst, TResult> firstSelector,
181+
Func<TSecond, TResult> secondSelector,
182+
Func<TFirst, TSecond, TResult> bothSelector,
183+
IComparer<TKey> comparer)
184+
{
185+
if (first == null) throw new ArgumentNullException("first");
186+
if (second == null) throw new ArgumentNullException("second");
187+
if (firstKeySelector == null) throw new ArgumentNullException("firstKeySelector");
188+
if (secondKeySelector == null) throw new ArgumentNullException("secondKeySelector");
189+
if (firstSelector == null) throw new ArgumentNullException("firstSelector");
190+
if (bothSelector == null) throw new ArgumentNullException("bothSelector");
191+
if (secondSelector == null) throw new ArgumentNullException("secondSelector");
192+
193+
return OrderedMergeImpl(first, second,
194+
firstKeySelector, secondKeySelector,
195+
firstSelector, secondSelector, bothSelector,
196+
comparer ?? Comparer<TKey>.Default);
197+
}
198+
199+
static IEnumerable<TResult> OrderedMergeImpl<TFirst, TSecond, TKey, TResult>(
200+
IEnumerable<TFirst> first,
201+
IEnumerable<TSecond> second,
202+
Func<TFirst, TKey> firstKeySelector,
203+
Func<TSecond, TKey> secondKeySelector,
204+
Func<TFirst, TResult> firstSelector,
205+
Func<TSecond, TResult> secondSelector,
206+
Func<TFirst, TSecond, TResult> bothSelector,
207+
IComparer<TKey> comparer)
208+
{
209+
Debug.Assert(first != null);
210+
Debug.Assert(second != null);
211+
Debug.Assert(firstKeySelector != null);
212+
Debug.Assert(secondKeySelector != null);
213+
Debug.Assert(firstSelector != null);
214+
Debug.Assert(secondSelector != null);
215+
Debug.Assert(bothSelector != null);
216+
Debug.Assert(comparer != null);
217+
218+
using (var e1 = first.GetEnumerator())
219+
using (var e2 = second.GetEnumerator())
220+
{
221+
var gotFirst = e1.MoveNext();
222+
var gotSecond = e2.MoveNext();
223+
224+
while (gotFirst || gotSecond)
225+
{
226+
if (gotFirst && gotSecond)
227+
{
228+
var element1 = e1.Current;
229+
var key1 = firstKeySelector(element1);
230+
var element2 = e2.Current;
231+
var key2 = secondKeySelector(element2);
232+
var comparison = comparer.Compare(key1, key2);
233+
234+
if (comparison < 0)
235+
{
236+
yield return firstSelector(element1);
237+
gotFirst = e1.MoveNext();
238+
}
239+
else if (comparison > 0)
240+
{
241+
yield return secondSelector(element2);
242+
gotSecond = e2.MoveNext();
243+
}
244+
else
245+
{
246+
yield return bothSelector(element1, element2);
247+
gotFirst = e1.MoveNext();
248+
gotSecond = e2.MoveNext();
249+
}
250+
}
251+
else if (gotSecond)
252+
{
253+
yield return secondSelector(e2.Current);
254+
gotSecond = e2.MoveNext();
255+
}
256+
else // (gotFirst)
257+
{
258+
yield return firstSelector(e1.Current);
259+
gotFirst = e1.MoveNext();
260+
}
261+
}
262+
}
263+
}
264+
}
265+
}

0 commit comments

Comments
 (0)