diff --git a/MoreLinq.Test/ExceptAllByTest.cs b/MoreLinq.Test/ExceptAllByTest.cs new file mode 100644 index 000000000..5b793f6d2 --- /dev/null +++ b/MoreLinq.Test/ExceptAllByTest.cs @@ -0,0 +1,129 @@ +#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 + +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace MoreLinq.Test +{ + [TestFixture] + public class ExceptAllByTest + { + [Test] + public void SimpleExceptAllBy() + { + string[] first = { "aaa", "bb", "c", "dddd" }; + string[] second = { "xx", "y" }; + var result = first.ExceptAllBy(second, x => x.Length); + result.AssertSequenceEqual("aaa", "dddd"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllByNullFirstSequence() + { + string[] first = null; + string[] second = { "aaa" }; + first.ExceptAllBy(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllByNullSecondSequence() + { + string[] first = { "aaa" }; + string[] second = null; + first.ExceptAllBy(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllByNullKeySelector() + { + string[] first = { "aaa" }; + string[] second = { "aaa" }; + first.ExceptAllBy(second, (Func)null); + } + + [Test] + public void ExceptAllByIsLazy() + { + new BreakingSequence().ExceptAllBy(new string[0], x => x.Length); + } + + [Test] + public void ExceptAllByRepeatsSourceElementsWithDuplicateKeys() + { + string[] first = { "aaa", "bb", "c", "a", "b", "c", "dddd" }; + string[] second = { "xx" }; + var result = first.ExceptAllBy(second, x => x.Length); + result.AssertSequenceEqual("aaa", "c", "a", "b", "c", "dddd"); + } + + [Test] + public void ExceptAllByWithComparer() + { + string[] first = { "first", "second", "third", "fourth" }; + string[] second = { "FIRST", "thiRD", "FIFTH" }; + var result = first.ExceptBy(second, word => word, StringComparer.OrdinalIgnoreCase); + result.AssertSequenceEqual("second", "fourth"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllByNullFirstSequenceWithComparer() + { + string[] first = null; + string[] second = { "aaa" }; + first.ExceptAllBy(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllByNullSecondSequenceWithComparer() + { + string[] first = { "aaa" }; + string[] second = null; + first.ExceptAllBy(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllByNullKeySelectorWithComparer() + { + string[] first = { "aaa" }; + string[] second = { "aaa" }; + first.ExceptAllBy(second, null, EqualityComparer.Default); + } + + [Test] + public void ExceptAllByNullComparer() + { + string[] first = { "aaa", "bb", "bb", "c", "dddd", "dddd" }; + string[] second = { "xx", "y" }; + var result = first.ExceptAllBy(second, x => x.Length, null); + result.AssertSequenceEqual("aaa", "dddd", "dddd"); + } + + [Test] + public void ExceptAllByIsLazyWithComparer() + { + new BreakingSequence().ExceptAllBy(new string[0], x => x, StringComparer.Ordinal); + } + } +} diff --git a/MoreLinq.Test/ExceptAllKeysTest.cs b/MoreLinq.Test/ExceptAllKeysTest.cs new file mode 100644 index 000000000..f991300c3 --- /dev/null +++ b/MoreLinq.Test/ExceptAllKeysTest.cs @@ -0,0 +1,129 @@ +#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 + +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace MoreLinq.Test +{ + [TestFixture] + public class ExceptAllKeysTest + { + [Test] + public void SimpleExceptAllKeys() + { + string[] first = { "aaa", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.ExceptAllKeys(second, x => x.Length); + result.AssertSequenceEqual("aaa", "dddd"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllKeysNullFirstSequence() + { + string[] first = null; + int[] second = { 1 }; + first.ExceptAllKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllKeysNullSecondSequence() + { + string[] first = { "aaa" }; + int[] second = null; + first.ExceptAllKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllKeysNullKeySelector() + { + string[] first = { "aaa" }; + int[] second = { 1 }; + first.ExceptAllKeys(second, (Func)null); + } + + [Test] + public void ExceptAllKeysIsLazy() + { + new BreakingSequence().ExceptAllKeys(new int[0], x => x.Length); + } + + [Test] + public void ExceptAllKeysRepeatsSourceElementsWithDuplicateKeys() + { + string[] first = { "aaa", "bb", "c", "a", "b", "c", "dddd" }; + int[] second = { 2 }; + var result = first.ExceptAllKeys(second, x => x.Length); + result.AssertSequenceEqual("aaa", "c", "a", "b", "c", "dddd"); + } + + [Test] + public void ExceptAllKeysWithComparer() + { + string[] first = { "first", "second", "third", "fourth" }; + string[] second = { "FIRST" , "thiRD", "FIFTH" }; + var result = first.ExceptAllKeys(second, word => word, StringComparer.OrdinalIgnoreCase); + result.AssertSequenceEqual("second", "fourth"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllKeysNullFirstSequenceWithComparer() + { + string[] first = null; + int[] second = { 1 }; + first.ExceptAllKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllKeysNullSecondSequenceWithComparer() + { + string[] first = { "aaa" }; + int[] second = null; + first.ExceptAllKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptAllKeysNullKeySelectorWithComparer() + { + string[] first = { "aaa" }; + int[] second = { 1 }; + first.ExceptAllKeys(second, null, EqualityComparer.Default); + } + + [Test] + public void ExceptAllKeysNullComparer() + { + string[] first = { "aaa", "aaa", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.ExceptAllKeys(second, x => x.Length, null); + result.AssertSequenceEqual("aaa", "aaa", "dddd"); + } + + [Test] + public void ExceptAllKeysIsLazyWithComparer() + { + new BreakingSequence().ExceptAllKeys(new string[0], x => x, StringComparer.Ordinal); + } + } +} diff --git a/MoreLinq.Test/ExceptKeysTest.cs b/MoreLinq.Test/ExceptKeysTest.cs new file mode 100644 index 000000000..64839bacd --- /dev/null +++ b/MoreLinq.Test/ExceptKeysTest.cs @@ -0,0 +1,129 @@ +#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 + +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace MoreLinq.Test +{ + [TestFixture] + public class ExceptKeysTest + { + [Test] + public void SimpleExceptKeys() + { + string[] first = { "aaa", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.ExceptKeys(second, x => x.Length); + result.AssertSequenceEqual("aaa", "dddd"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptKeysNullFirstSequence() + { + string[] first = null; + int[] second = { 1 }; + first.ExceptKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptKeysNullSecondSequence() + { + string[] first = { "aaa" }; + int[] second = null; + first.ExceptKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptKeysNullKeySelector() + { + string[] first = { "aaa" }; + int[] second = { 1 }; + first.ExceptKeys(second, (Func)null); + } + + [Test] + public void ExceptKeysIsLazy() + { + new BreakingSequence().ExceptKeys(new int[0], x => x.Length); + } + + [Test] + public void ExceptKeysDoesNotRepeatSourceElementsWithDuplicateKeys() + { + string[] first = { "aaa", "bb", "c", "a", "b", "c", "dddd" }; + int[] second = { 2 }; + var result = first.ExceptKeys(second, x => x.Length); + result.AssertSequenceEqual("aaa", "c", "dddd"); + } + + [Test] + public void ExceptKeysWithComparer() + { + string[] first = { "first", "second", "third", "fourth" }; + string[] second = { "FIRST" , "thiRD", "FIFTH" }; + var result = first.ExceptKeys(second, word => word, StringComparer.OrdinalIgnoreCase); + result.AssertSequenceEqual("second", "fourth"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptKeysNullFirstSequenceWithComparer() + { + string[] first = null; + int[] second = { 1 }; + first.ExceptKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptKeysNullSecondSequenceWithComparer() + { + string[] first = { "aaa" }; + int[] second = null; + first.ExceptKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ExceptKeysNullKeySelectorWithComparer() + { + string[] first = { "aaa" }; + int[] second = { 1 }; + first.ExceptKeys(second, null, EqualityComparer.Default); + } + + [Test] + public void ExceptKeysNullComparer() + { + string[] first = { "aaa", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.ExceptKeys(second, x => x.Length, null); + result.AssertSequenceEqual("aaa", "dddd"); + } + + [Test] + public void ExceptKeysIsLazyWithComparer() + { + new BreakingSequence().ExceptKeys(new string[0], x => x, StringComparer.Ordinal); + } + } +} diff --git a/MoreLinq.Test/IntersectAllByTest.cs b/MoreLinq.Test/IntersectAllByTest.cs new file mode 100644 index 000000000..50a07f51f --- /dev/null +++ b/MoreLinq.Test/IntersectAllByTest.cs @@ -0,0 +1,115 @@ +#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 + +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace MoreLinq.Test { + [TestFixture] + public class IntersectAllByTest { + [Test] + public void SimpleIntersectAllBy() { + string[] first = { "aaa", "bb", "c", "dddd" }; + string[] second = { "bb", "c" }; + var result = first.IntersectAllBy(second, x => x.Length); + result.AssertSequenceEqual("bb", "c"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllByNullFirstSequence() { + string[] first = null; + string[] second = { "aaa" }; + first.IntersectAllBy(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllByNullSecondSequence() { + string[] first = { "aaa" }; + string[] second = null; + first.IntersectAllBy(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllByNullKeySelector() { + string[] first = { "aaa" }; + string[] second = { "aaa" }; + first.IntersectAllBy(second, (Func)null); + } + + [Test] + public void IntersectAllByIsLazy() { + new BreakingSequence().IntersectAllBy(new string[0], x => x.Length); + } + + [Test] + public void IntersectAllByRepeatsSourceElementsWithDuplicateKeys() { + string[] first = { "aaa", "bb", "c", "a", "b", "c", "dddd" }; + string[] second = { "c" }; + var result = first.IntersectAllBy(second, x => x.Length); + result.AssertSequenceEqual("c", "a", "b", "c"); + } + + [Test] + public void IntersectAllByWithComparer() { + string[] first = { "first", "second", "third", "fourth" }; + string[] second = { "FIRST", "thiRD", "FIFTH" }; + var result = first.IntersectAllBy(second, word => word, StringComparer.OrdinalIgnoreCase); + result.AssertSequenceEqual("first", "third"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllByNullFirstSequenceWithComparer() { + string[] first = null; + string[] second = { "aaa" }; + first.IntersectAllBy(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllByNullSecondSequenceWithComparer() { + string[] first = { "aaa" }; + string[] second = null; + first.IntersectAllBy(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllByNullKeySelectorWithComparer() { + string[] first = { "aaa" }; + string[] second = { "aaa" }; + first.IntersectAllBy(second, null, EqualityComparer.Default); + } + + [Test] + public void IntersectAllByNullComparer() { + string[] first = { "aaa", "bb", "bb", "c", "dddd" }; + string[] second = { "xx", "y" }; + var result = first.IntersectAllBy(second, x => x.Length, null); + result.AssertSequenceEqual("bb","bb", "c"); + } + + [Test] + public void IntersectAllByIsLazyWithComparer() { + new BreakingSequence().IntersectAllBy(new string[0], x => x, StringComparer.Ordinal); + } + } +} diff --git a/MoreLinq.Test/IntersectAllKeysTest.cs b/MoreLinq.Test/IntersectAllKeysTest.cs new file mode 100644 index 000000000..70f625b66 --- /dev/null +++ b/MoreLinq.Test/IntersectAllKeysTest.cs @@ -0,0 +1,115 @@ +#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 + +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace MoreLinq.Test { + [TestFixture] + public class IntersectAllKeysTest { + [Test] + public void SimpleIntersectAllKeys() { + string[] first = { "aaa", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.IntersectAllKeys(second, x => x.Length); + result.AssertSequenceEqual("bb", "c"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllKeysNullFirstSequence() { + string[] first = null; + int[] second = { 1 }; + first.IntersectAllKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllKeysNullSecondSequence() { + string[] first = { "aaa" }; + int[] second = null; + first.IntersectAllKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllKeysNullKeySelector() { + string[] first = { "aaa" }; + int[] second = { 3 }; + first.IntersectAllKeys(second, (Func)null); + } + + [Test] + public void IntersectAllKeysIsLazy() { + new BreakingSequence().IntersectAllKeys(new int[0], x => x.Length); + } + + [Test] + public void IntersectAllKeysRepeatsSourceElementsWithDuplicateKeys() { + string[] first = { "aaa", "bb", "c", "a", "b", "c", "dddd" }; + int[] second = { 1 }; + var result = first.IntersectAllKeys(second, x => x.Length); + result.AssertSequenceEqual("c", "a", "b", "c"); + } + + [Test] + public void IntersectAllKeysWithComparer() { + string[] first = { "first", "second", "third", "fourth" }; + string[] second = { "FIRST", "thiRD", "FIFTH" }; + var result = first.IntersectAllKeys(second, word => word, StringComparer.OrdinalIgnoreCase); + result.AssertSequenceEqual("first", "third"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllKeysNullFirstSequenceWithComparer() { + string[] first = null; + int[] second = { 1 }; + first.IntersectAllKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllKeysNullSecondSequenceWithComparer() { + string[] first = { "aaa" }; + int[] second = null; + first.IntersectAllKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectAllKeysNullKeySelectorWithComparer() { + string[] first = { "aaa" }; + int[] second = { 1 }; + first.IntersectAllKeys(second, null, EqualityComparer.Default); + } + + [Test] + public void IntersectAllKeysNullComparer() { + string[] first = { "aaa", "bb", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.IntersectAllKeys(second, x => x.Length, null); + result.AssertSequenceEqual("bb", "bb", "c"); + } + + [Test] + public void IntersectAllKeysIsLazyWithComparer() { + new BreakingSequence().IntersectAllKeys(new string[0], x => x, StringComparer.Ordinal); + } + } +} diff --git a/MoreLinq.Test/IntersectByTest.cs b/MoreLinq.Test/IntersectByTest.cs new file mode 100644 index 000000000..a77cab2bb --- /dev/null +++ b/MoreLinq.Test/IntersectByTest.cs @@ -0,0 +1,115 @@ +#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 + +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace MoreLinq.Test { + [TestFixture] + public class IntersectByTest { + [Test] + public void SimpleIntersectBy() { + string[] first = { "aaa", "bb", "c", "dddd" }; + string[] second = { "bb", "c" }; + var result = first.IntersectBy(second, x => x.Length); + result.AssertSequenceEqual("bb", "c"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectByNullFirstSequence() { + string[] first = null; + string[] second = { "aaa" }; + first.IntersectBy(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectByNullSecondSequence() { + string[] first = { "aaa" }; + string[] second = null; + first.IntersectBy(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectByNullKeySelector() { + string[] first = { "aaa" }; + string[] second = { "aaa" }; + first.IntersectBy(second, (Func)null); + } + + [Test] + public void IntersectByIsLazy() { + new BreakingSequence().IntersectBy(new string[0], x => x.Length); + } + + [Test] + public void IntersectByDoesNotRepeatSourceElementsWithDuplicateKeys() { + string[] first = { "aaa", "bb", "c", "a", "b", "c", "dddd" }; + string[] second = { "c" }; + var result = first.IntersectBy(second, x => x.Length); + result.AssertSequenceEqual("c"); + } + + [Test] + public void IntersectByWithComparer() { + string[] first = { "first", "second", "third", "fourth" }; + string[] second = { "FIRST", "thiRD", "FIFTH" }; + var result = first.IntersectBy(second, word => word, StringComparer.OrdinalIgnoreCase); + result.AssertSequenceEqual("first", "third"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectByNullFirstSequenceWithComparer() { + string[] first = null; + string[] second = { "aaa" }; + first.IntersectBy(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectByNullSecondSequenceWithComparer() { + string[] first = { "aaa" }; + string[] second = null; + first.IntersectBy(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectByNullKeySelectorWithComparer() { + string[] first = { "aaa" }; + string[] second = { "aaa" }; + first.IntersectBy(second, null, EqualityComparer.Default); + } + + [Test] + public void IntersectByNullComparer() { + string[] first = { "aaa", "bb", "c", "dddd" }; + string[] second = { "xx", "y" }; + var result = first.IntersectBy(second, x => x.Length, null); + result.AssertSequenceEqual("bb", "c"); + } + + [Test] + public void IntersectByIsLazyWithComparer() { + new BreakingSequence().IntersectBy(new string[0], x => x, StringComparer.Ordinal); + } + } +} diff --git a/MoreLinq.Test/IntersectKeysTest.cs b/MoreLinq.Test/IntersectKeysTest.cs new file mode 100644 index 000000000..bc75e8eda --- /dev/null +++ b/MoreLinq.Test/IntersectKeysTest.cs @@ -0,0 +1,115 @@ +#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 + +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace MoreLinq.Test { + [TestFixture] + public class IntersectKeysTest { + [Test] + public void SimpleIntersectKeys() { + string[] first = { "aaa", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.IntersectKeys(second, x => x.Length); + result.AssertSequenceEqual("bb", "c"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectKeysNullFirstSequence() { + string[] first = null; + int[] second = { 1 }; + first.IntersectKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectKeysNullSecondSequence() { + string[] first = { "aaa" }; + int[] second = null; + first.IntersectKeys(second, x => x.Length); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectKeysNullKeySelector() { + string[] first = { "aaa" }; + int[] second = { 3 }; + first.IntersectKeys(second, (Func)null); + } + + [Test] + public void IntersectKeysIsLazy() { + new BreakingSequence().IntersectKeys(new int[0], x => x.Length); + } + + [Test] + public void IntersectKeysDoesNotRepeatSourceElementsWithDuplicateKeys() { + string[] first = { "aaa", "bb", "c", "a", "b", "c", "dddd" }; + int[] second = { 1 }; + var result = first.IntersectKeys(second, x => x.Length); + result.AssertSequenceEqual("c"); + } + + [Test] + public void IntersectKeysWithComparer() { + string[] first = { "first", "second", "third", "fourth" }; + string[] second = { "FIRST", "thiRD", "FIFTH" }; + var result = first.IntersectKeys(second, word => word, StringComparer.OrdinalIgnoreCase); + result.AssertSequenceEqual("first", "third"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectKeysNullFirstSequenceWithComparer() { + string[] first = null; + int[] second = { 1 }; + first.IntersectKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectKeysNullSecondSequenceWithComparer() { + string[] first = { "aaa" }; + int[] second = null; + first.IntersectKeys(second, x => x.Length, EqualityComparer.Default); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void IntersectKeysNullKeySelectorWithComparer() { + string[] first = { "aaa" }; + int[] second = { 1 }; + first.IntersectKeys(second, null, EqualityComparer.Default); + } + + [Test] + public void IntersectKeysNullComparer() { + string[] first = { "aaa", "bb", "c", "dddd" }; + int[] second = { 1, 2 }; + var result = first.IntersectKeys(second, x => x.Length, null); + result.AssertSequenceEqual("bb", "c"); + } + + [Test] + public void IntersectKeysIsLazyWithComparer() { + new BreakingSequence().IntersectKeys(new string[0], x => x, StringComparer.Ordinal); + } + } +} diff --git a/MoreLinq/ExceptAll.cs b/MoreLinq/ExceptAll.cs new file mode 100644 index 000000000..ee6ea56fa --- /dev/null +++ b/MoreLinq/ExceptAll.cs @@ -0,0 +1,54 @@ +#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.Generic; + + static partial class MoreEnumerable { + + /// + /// Returns the sequence of elements in the first sequence which aren't + /// in the second sequence. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in are equal, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of elements from is cached as soon as execution begins. + /// Duplicate values in are not relevant and are discarded when is cached. + /// + /// The type of the elements of the input sequences. + /// The sequence of potentially included elements. + /// The set of elements which may prevent elements in from being returned. + /// The element comparer. If null, uses the default equality comparer for . + /// The sequence of elements from that are not in , + /// including duplicate values from . + /// Thrown if or is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable ExceptAll(this IEnumerable first, + IEnumerable second, + IEqualityComparer comparer) { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + + return SetFilterImpl(first, second, x => x, comparer, (hs, k) => !hs.Contains(k)); + } + + } +} diff --git a/MoreLinq/ExceptAllBy.cs b/MoreLinq/ExceptAllBy.cs new file mode 100644 index 000000000..53a7cec7f --- /dev/null +++ b/MoreLinq/ExceptAllBy.cs @@ -0,0 +1,90 @@ +#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.Generic; + using System.Linq; + + static partial class MoreEnumerable { + + /// + /// Returns the sequence of elements in the first sequence which aren't + /// in the second sequence, according to a given key selector. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in have equal keys, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of the elements of the input sequences. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The sequence of elements whose keys may prevent elements in from being returned. + /// The mapping from source element to key. + /// The sequence of elements from whose key was not also a key for + /// any element in , including values with duplicate keys from . + /// Thrown if , , or + /// is null. + public static IEnumerable ExceptAllBy(this IEnumerable first, + IEnumerable second, + Func keySelector) { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second.Select(keySelector), keySelector, null, (hs, k) => !hs.Contains(k)); + } + + /// + /// Returns the sequence of elements in the first sequence which aren't + /// in the second sequence, according to a given key selector and equality comparer. + /// + /// + /// This is not a set operation like ; + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of the elements in the input sequences. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The sequence of elements whose keys may prevent elements in from being returned. + /// The mapping from source element to key. + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TSource is used. + /// The sequence of elements from whose key was not also a key for + /// any element in , including values with duplicate keys from . + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable ExceptAllBy(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second.Select(keySelector), keySelector, keyComparer, (hs, k) => !hs.Contains(k)); + } + } +} diff --git a/MoreLinq/ExceptAllKeys.cs b/MoreLinq/ExceptAllKeys.cs new file mode 100644 index 000000000..c6d7ebbeb --- /dev/null +++ b/MoreLinq/ExceptAllKeys.cs @@ -0,0 +1,91 @@ +#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.Generic; + + static partial class MoreEnumerable { + + /// + /// Returns the sequence of elements in the first sequence, + /// whose keys are not in the second sequence, according to a given key selector. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in have equal keys, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The set of keys which may prevent elements in from being returned. + /// The mapping from source element to key. + /// The sequence of elements from whose key is not in , + /// including values with duplicate keys from . + /// Thrown if , , or + /// is null. + public static IEnumerable ExceptAllKeys(this IEnumerable first, + IEnumerable second, + Func keySelector) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, null, (hs, k) => !hs.Contains(k)); + } + + /// + /// Returns the sequence of elements from the first collection, + /// whose keys are not in the second collection, according to a given key selector and equality comparer. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in have equal keys, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The set of keys which may prevent elements in from being returned. + /// The mapping from source element to key. + /// The key comparer. If null, uses the default TKey equality comparer. + /// The sequence of elements from whose key is not in , + /// including values with duplicate keys from . + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable ExceptAllKeys(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, keyComparer, (hs, k) => !hs.Contains(k)); + } + } +} diff --git a/MoreLinq/ExceptBy.cs b/MoreLinq/ExceptBy.cs index ddc378cf9..bc07ea5d8 100644 --- a/MoreLinq/ExceptBy.cs +++ b/MoreLinq/ExceptBy.cs @@ -15,88 +15,75 @@ // limitations under the License. #endregion -namespace MoreLinq -{ +namespace MoreLinq { using System; using System.Collections.Generic; using System.Linq; - static partial class MoreEnumerable - { + static partial class MoreEnumerable { + /// - /// Returns the set of elements in the first sequence which aren't + /// Returns the set of distinct elements in the first sequence which aren't /// in the second sequence, according to a given key selector. /// /// /// This is a set operation; if multiple elements in have /// equal keys, only the first such element is returned. - /// This operator uses deferred execution and streams the results, although - /// a set of keys from is immediately selected and retained. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. /// /// The type of the elements in the input sequences. - /// The type of the key returned by . - /// The sequence of potentially included elements. - /// The sequence of elements whose keys may prevent elements in - /// from being returned. + /// The type of the key returned by , and used for equality comparison. + /// The set of potentially included elements. + /// The set of elements whose keys may prevent elements in from being returned. /// The mapping from source element to key. - /// A sequence of elements from whose key was not also a key for + /// The set of distinct elements from whose key was not also a key for /// any element in . - + /// Thrown if , , or + /// is null. public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, - Func keySelector) - { - return ExceptBy(first, second, keySelector, null); + Func keySelector) { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second.Select(keySelector), keySelector, null, (hs, k) => hs.Add(k)); } /// - /// Returns the set of elements in the first sequence which aren't - /// in the second sequence, according to a given key selector. + /// Returns the set of distinct elements in the first sequence which aren't + /// in the second sequence, according to a given key selector and equality comparer. /// /// /// This is a set operation; if multiple elements in have /// equal keys, only the first such element is returned. - /// This operator uses deferred execution and streams the results, although - /// a set of keys from is immediately selected and retained. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. /// /// The type of the elements in the input sequences. - /// The type of the key returned by . - /// The sequence of potentially included elements. - /// The sequence of elements whose keys may prevent elements in - /// from being returned. + /// The type of the key returned by , used for equality comparison. + /// The set of potentially included elements. + /// The set of elements whose keys may prevent elements in from being returned. /// The mapping from source element to key. /// The equality comparer to use to determine whether or not keys are equal. /// If null, the default equality comparer for TSource is used. - /// A sequence of elements from whose key was not also a key for + /// The set of distinct elements from whose key was not also a key for /// any element in . - + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector, - IEqualityComparer keyComparer) - { + IEqualityComparer keyComparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - return ExceptByImpl(first, second, keySelector, keyComparer); - } - private static IEnumerable ExceptByImpl(this IEnumerable first, - IEnumerable second, - Func keySelector, - IEqualityComparer keyComparer) - { - var keys = new HashSet(second.Select(keySelector), keyComparer); - foreach (var element in first) - { - var key = keySelector(element); - if (keys.Contains(key)) - { - continue; - } - yield return element; - keys.Add(key); - } + return SetFilterImpl(first, second.Select(keySelector), keySelector, keyComparer, (hs, k) => hs.Add(k)); } } } diff --git a/MoreLinq/ExceptKeys.cs b/MoreLinq/ExceptKeys.cs new file mode 100644 index 000000000..a965f6cd8 --- /dev/null +++ b/MoreLinq/ExceptKeys.cs @@ -0,0 +1,88 @@ +#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.Generic; + + static partial class MoreEnumerable { + + /// + /// Returns the set of distinct elements in the first sequence, + /// whose keys are not in the second sequence, according to a given key selector. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by , used for equality comparison. + /// The set of potentially included elements. + /// The set of keys which may prevent elements in from being returned. + /// The mapping from source element to key. + /// The set of distinct elements from whose key is not in . + /// Thrown if , , or + /// is null. + public static IEnumerable ExceptKeys(this IEnumerable first, + IEnumerable second, + Func keySelector) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, null, (hs, k) => hs.Add(k)); + } + + /// + /// Returns the set of distinct elements in the first sequence, + /// whose keys are not in the second sequence, according to a given key selector and equality comparer. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by , used for equality comparison. + /// The set of potentially included elements. + /// The set of keys which may prevent elements in from being returned. + /// The mapping from source element to key. + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TSource is used. + /// The set of distinct elements from whose key is not in . + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable ExceptKeys(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, keyComparer, (hs, k) => hs.Add(k)); + } + } +} diff --git a/MoreLinq/IntersectAll.cs b/MoreLinq/IntersectAll.cs new file mode 100644 index 000000000..bb3f9656d --- /dev/null +++ b/MoreLinq/IntersectAll.cs @@ -0,0 +1,54 @@ +#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.Generic; + + partial class MoreEnumerable { + + /// + /// Returns the sequence of elements in the first sequence which are also in the second sequence. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in are equal, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of elements from is cached as soon as execution begins. + /// Duplicate values in are not relevant and are discarded when is cached. + /// + /// The type of the elements of the input sequences. + /// The sequence of potentially included elements. + /// The set of elements which may allow elements in to be returned. + /// The element comparer. If null, uses the default equality comparer for . + /// The sequence of elements from that are also in , + /// including duplicate values from . + /// Thrown if or is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable IntersectAll(this IEnumerable first, + IEnumerable second, + IEqualityComparer comparer) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + + return SetFilterImpl(first, second, x => x, comparer, (hs, k) => hs.Contains(k)); + } + + } +} diff --git a/MoreLinq/IntersectAllBy.cs b/MoreLinq/IntersectAllBy.cs new file mode 100644 index 000000000..9424afb39 --- /dev/null +++ b/MoreLinq/IntersectAllBy.cs @@ -0,0 +1,92 @@ +#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.Generic; + using System.Linq; + + partial class MoreEnumerable { + + /// + /// Returns the sequence of elements in the first sequence which are also + /// in the second sequence, according to a given key selector. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in have equal keys, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of the elements of the input sequences. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The sequence of elements whose keys may allow elements in to be returned. + /// The mapping from source element to key. + /// The sequence of elements from whose key was also a key for + /// any element in , including values with duplicate keys from . + /// Thrown if , , or + /// is null. + public static IEnumerable IntersectAllBy(this IEnumerable first, + IEnumerable second, + Func keySelector) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second.Select(keySelector), keySelector, null, (hs, k) => hs.Contains(k)); + } + + /// + /// Returns the sequence of elements in the first sequence which are also + /// in the second sequence, according to a given key selector and equality comparer. + /// + /// + /// This is not a set operation like ; + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of the elements in the input sequences. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The sequence of elements whose keys may allow elements in to be returned. + /// The mapping from source element to key. + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TSource is used. + /// The sequence of elements from whose key was also a key for + /// any element in , including values with duplicate keys from . + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable IntersectAllBy(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second.Select(keySelector), keySelector, keyComparer, (hs, k) => hs.Contains(k)); + } + } +} diff --git a/MoreLinq/IntersectAllKeys.cs b/MoreLinq/IntersectAllKeys.cs new file mode 100644 index 000000000..f75e9cf11 --- /dev/null +++ b/MoreLinq/IntersectAllKeys.cs @@ -0,0 +1,91 @@ +#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.Generic; + + partial class MoreEnumerable { + + /// + /// Returns the sequence of elements in the first sequence, + /// whose keys are in the second sequence, according to a given key selector. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in have equal keys, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The set of keys which may allow elements in to be returned. + /// The mapping from source element to key. + /// The sequence of elements from whose key is in , + /// including values with duplicate keys from . + /// Thrown if , , or + /// is null. + public static IEnumerable IntersectAllKeys(this IEnumerable first, + IEnumerable second, + Func keySelector) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, null, (hs, k) => hs.Contains(k)); + } + + /// + /// Returns the sequence of elements from the first collection, + /// whose keys are in the second collection, according to a given key selector and equality comparer. + /// + /// + /// This is not a set operation like ; + /// if multiple elements in have equal keys, all such elements are returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by and used for comparing values. + /// The sequence of potentially included elements. + /// The set of keys which may allow elements in to be returned. + /// The mapping from source element to key. + /// The key comparer. If null, uses the default TKey equality comparer. + /// The sequence of elements from whose key is in , + /// including values with duplicate keys from . + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable IntersectAllKeys(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, keyComparer, (hs, k) => hs.Contains(k)); + } + } +} diff --git a/MoreLinq/IntersectBy.cs b/MoreLinq/IntersectBy.cs new file mode 100644 index 000000000..5d4af36fc --- /dev/null +++ b/MoreLinq/IntersectBy.cs @@ -0,0 +1,91 @@ +#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.Generic; + using System.Linq; + + partial class MoreEnumerable { + + /// + /// Returns the set of distinct elements from the first sequence which are also + /// in the second sequence, according to the given key selector. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of the source and result elements. + /// The type of the key returned by , and used for equality comparison. + /// The set of potentially included elements. + /// The set of elements whose keys may allow elements in to be returned. + /// The mapping from source element to key. + /// The set of distinct elements from whose key is also the key + /// of an element in , according to the given key selector. + /// Thrown if , , or + /// is null. + public static IEnumerable IntersectBy(this IEnumerable first, + IEnumerable second, + Func keySelector) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second.Select(keySelector), keySelector, null, (hs, k) => hs.Remove(k)); + } + + /// + /// Returns the set of distinct elements from the first sequence which are also + /// in the second sequence, according to the given key selector and equality comparer. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of the source and result elements. + /// The type of the key returned by , and used for equality comparison. + /// The set of potentially included elements. + /// The set of elements whose keys may allow elements in to be returned. + /// The mapping from source element to key. + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TSource is used. + /// The set of distinct elements from whose key is also the key + /// of an element in , according to the given key selector. + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable IntersectBy(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second.Select(keySelector), keySelector, keyComparer, (hs, k) => hs.Remove(k)); + } + } +} diff --git a/MoreLinq/IntersectKeys.cs b/MoreLinq/IntersectKeys.cs new file mode 100644 index 000000000..11aabbe33 --- /dev/null +++ b/MoreLinq/IntersectKeys.cs @@ -0,0 +1,88 @@ +#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.Generic; + + partial class MoreEnumerable { + + /// + /// Returns the set of distinct elements in the first sequence, + /// whose keys are in the second sequence, according to a given key selector. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by , used for equality comparison. + /// The set of potentially included elements. + /// The set of keys which may allow elements in to be returned. + /// The mapping from source element to key. + /// The set of distinct elements from whose key is in . + /// Thrown if , , or + /// is null. + public static IEnumerable IntersectKeys(this IEnumerable first, + IEnumerable second, + Func keySelector) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, null, (hs, k) => hs.Remove(k)); + } + + /// + /// Returns the set of distinct elements in the first sequence, + /// whose keys are in the second sequence, according to a given key selector and equality comparer. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams results from , + /// but the entire set of keys from is cached as soon as execution begins. + /// Duplicate keys from are not relevant and are discarded when is cached. + /// + /// The type of source and result elements. + /// The type of the key returned by , used for equality comparison. + /// The set of potentially included elements. + /// The set of keys which may allow elements in to be returned. + /// The mapping from source element to key. + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TSource is used. + /// The set of distinct elements from whose key is in . + /// Thrown if , , or + /// is null. + /// If is null, the default equality comparer for is used. + public static IEnumerable IntersectKeys(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) { + + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return SetFilterImpl(first, second, keySelector, keyComparer, (hs, k) => hs.Remove(k)); + } + } +} diff --git a/MoreLinq/SetFilter.cs b/MoreLinq/SetFilter.cs new file mode 100644 index 000000000..77cdf626d --- /dev/null +++ b/MoreLinq/SetFilter.cs @@ -0,0 +1,36 @@ +#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.Generic; + using System.Linq; + + partial class MoreEnumerable { + + //This method serves as the implementation of all Except and Intersect variations + private static IEnumerable SetFilterImpl(IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer, + Func, TKey, bool> keyTest) { + + var keys = new HashSet(second, keyComparer); + return first.Where(x => keyTest(keys, keySelector(x))); + } + } +}