Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ZipMap #763

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
55 changes: 55 additions & 0 deletions MoreLinq.Test/ZipMapTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#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.Test
{
using System.Text.RegularExpressions;
using NUnit.Framework;

[TestFixture]
public class ZipMapTest
{
[Test]
public void ZipMapIsLazy()
{
new BreakingSequence<object>().ZipMap(o => o);
}

[Test]
public void ZipMapEmptySequence()
{
object[] objects = {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use TestingSequence<> as the source since that asserts on disposal that ZipMap disposed it:

Suggested change
object[] objects = {};
using var objects = Enumerable.Empty<object>().AsTestingSequence();

var result = objects.ZipMap(o => o);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be more strict:

Suggested change
var result = objects.ZipMap(o => o);
var result = objects.ZipMap(BreakingFunc.Of<object, object>());

result.AssertSequenceEqual();
}

[Test]
public void ZipMapStrings()
{
string[] strings = { "foo", "bar", "baz" };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use TestingSequence<> as the source since that asserts on disposal that ZipMap disposed it:

Suggested change
string[] strings = { "foo", "bar", "baz" };
using var strings = TestingSequence.Of("foo", "bar", "baz");

var result = strings.ZipMap(s => Regex.IsMatch(s, @"^b"));
result.AssertSequenceEqual(("foo", false), ("bar", true), ("baz", true));
}

[Test]
public void ZipMapFromSequence()
{
var result = MoreEnumerable.Sequence(5, 8).ZipMap(i => i % 2 == 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use TestingSequence<> as the source since that asserts on disposal that ZipMap disposed it:

Suggested change
var result = MoreEnumerable.Sequence(5, 8).ZipMap(i => i % 2 == 0);
using var xs = TestingSequence.Of(5, 6, 7, 8);
var result = xs.ZipMap(i => i % 2 == 0);

result.AssertSequenceEqual((5, false), (6, true), (7, false), (8, true));
}
}
}
35 changes: 35 additions & 0 deletions MoreLinq/Extensions.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6844,6 +6844,41 @@ public static IEnumerable<TResult> ZipLongest<T1, T2, T3, T4, TResult>(

}

/// <summary><c>ZipMap</c> extension.</summary>

[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]
public static partial class ZipMapExtension
{
/// <summary>
/// Applies a function on each element and returns a sequence of
/// tuples with the source element and the result from the function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the elements returned by <paramref name="func"/>.</typeparam>
/// <param name="source">The sequence to iterate over.</param>
/// <param name="func">The function to apply on each element.</param>
/// <returns>
/// Returns a sequence of tuples with the source element and the
/// result from <paramref name="func"/> parameter.
/// </returns>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>
/// <example>
/// <code><![CDATA[
/// string[] strings = { "foo", "bar", "baz" };
/// var result = strings.ZipMap(s => Regex.IsMatch(s, @"^b"));
/// ]]></code>
/// The <c>result</c> variable, when iterated over, will yield
/// ("foo", false"), ("bar", true) and ("baz", true), in turn.
/// </example>

public static IEnumerable<(TSource Item, TResult Result)> ZipMap<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, TResult> func)
=> MoreEnumerable.ZipMap(source, func);

}

/// <summary><c>ZipShortest</c> extension.</summary>

[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]
Expand Down
1 change: 1 addition & 0 deletions MoreLinq/MoreLinq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
- WindowLeft
- WindowRight
- ZipLongest
- ZipMap
- ZipShortest
</Description>
<Description>$([System.Text.RegularExpressions.Regex]::Replace($(Description), `\s+`, ` `).Trim().Replace(` - `, `, `).Replace(`:,`, `:`))</Description>
Expand Down
61 changes: 61 additions & 0 deletions MoreLinq/ZipMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#region License and Terms
// MoreLINQ - Extensions to LINQ to Objects
// Copyright (c) 2008 Jonathan Skeet. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice has incorrect date and author

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Should be changed to:

Suggested change
// Copyright (c) 2008 Jonathan Skeet. All rights reserved.
// Copyright (c) 2020 Daniel Jonsson. 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
{
/// <summary>
/// Applies a function on each element and returns a sequence of
/// tuples with the source element and the result from the function.
Comment on lines +26 to +27
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Applies a function on each element and returns a sequence of
/// tuples with the source element and the result from the function.
/// Applies a function on each element of the sequence and returns a
/// sequence of tuples with the source element and the result from the
/// function.

/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the elements returned by <paramref name="func"/>.</typeparam>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// <typeparam name="TResult">The type of the elements returned by <paramref name="func"/>.</typeparam>
/// <typeparam name="TResult">The type of value returned by <paramref name="func"/>.</typeparam>

/// <param name="source">The sequence to iterate over.</param>
/// <param name="func">The function to apply on each element.</param>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// <param name="func">The function to apply on each element.</param>
/// <param name="func">The function to apply to each element.</param>

/// <returns>
/// Returns a sequence of tuples with the source element and the
/// result from <paramref name="func"/> parameter.
/// </returns>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>
/// <example>
/// <code><![CDATA[
/// string[] strings = { "foo", "bar", "baz" };
/// var result = strings.ZipMap(s => Regex.IsMatch(s, @"^b"));
/// ]]></code>
/// The <c>result</c> variable, when iterated over, will yield
/// ("foo", false"), ("bar", true) and ("baz", true), in turn.
Comment on lines +45 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The <c>result</c> variable, when iterated over, will yield
/// ("foo", false"), ("bar", true) and ("baz", true), in turn.
/// <para>
/// The <c>result</c> variable, when iterated over, will yield
/// <c>("foo", false)</c>, <c>("bar", true)</c> and
/// <c>("baz", true)</c>, in turn.</para>
  • Marks as a paragraph.
  • Marks each tuple as code (<c>).
  • Removes a quote after the first false.

/// </example>

public static IEnumerable<(TSource Item, TResult Result)> ZipMap<TSource, TResult>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the first tuple element should be named Source or Input instead of Item. Thoughts?

this IEnumerable<TSource> source, Func<TSource, TResult> func)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (func == null) throw new ArgumentNullException(nameof(func));

foreach (var item in source)
{
yield return (item, func(item));
}
Comment on lines +55 to +58
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NullArgumentTest tests are breaking. The fix is to validate the arguments eagerly and wrap the iterator in a local function as follows (otherwise arguments are validated when the sequence is iterated rather than when ZipMap is called):

Suggested change
foreach (var item in source)
{
yield return (item, func(item));
}
return _(); IEnumerable<(TSource, TResult)> _()
{
foreach (var item in source)
{
yield return (item, func(item));
}
}

}
}
}
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,11 @@ for padding.

This method has 3 overloads.

### ZipMap

Returns a sequence of tuples containing the source elements and the result of
a function applied on each element.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
a function applied on each element.
a function applied to each element.


### ZipShortest

Returns a projection of tuples, where each tuple contains the N-th
Expand Down