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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
**/bin
**/obj
*.vs
TestResults
TestResults
**/**.DotSettings.user
NuGet.config
48 changes: 37 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,54 @@
# Introduction

[![Build Status](https://dev.azure.com/dimenicsbe/Utilities/_apis/build/status/Linq%20-%20MAIN%20-%20CI?branchName=master)](https://dev.azure.com/dimenicsbe/Utilities/_build/latest?definitionId=105&branchName=master) [![Dime.Linq package in Dime.Scheduler feed in Azure Artifacts](https://feeds.dev.azure.com/dimenicsbe/_apis/public/Packaging/Feeds/a7b896fd-9cd8-4291-afe1-f223483d87f0/Packages/a4ea1a44-b4ee-49dd-ba2f-eff013a1c9ce/Badge)](https://dev.azure.com/dimenicsbe/Utilities/_packaging?_a=package&feed=a7b896fd-9cd8-4291-afe1-f223483d87f0&package=a4ea1a44-b4ee-49dd-ba2f-eff013a1c9ce&preferRelease=true)
![Build Status](https://dev.azure.com/dimenicsbe/Utilities/_apis/build/status/dimenics.dime-linq?branchName=master) [![Dime.Linq package in Dime.Scheduler feed in Azure Artifacts](https://feeds.dev.azure.com/dimenicsbe/_apis/public/Packaging/Feeds/a7b896fd-9cd8-4291-afe1-f223483d87f0/Packages/a4ea1a44-b4ee-49dd-ba2f-eff013a1c9ce/Badge)](https://dev.azure.com/dimenicsbe/Utilities/_packaging?_a=package&feed=a7b896fd-9cd8-4291-afe1-f223483d87f0&package=a4ea1a44-b4ee-49dd-ba2f-eff013a1c9ce&preferRelease=true)

Dime.Linq is a utility library which contains a set of extensions for the System.Linq namespace.

# Getting Started
## Getting Started

You can clone the repo by running this command:
- You must have Visual Studio 2019 Community or higher.
- The dotnet cli is also highly recommended.

`git clone https://dimenicsbe@dev.azure.com/dimenicsbe/Utilities/_git/Dime.Linq `
## About this project

# Build and Test
Some interesting extension methods in this assembly include:

Visual Studio 2019 is the recommended IDE to browse and run the code. MSTest is the testing framework.
- `Fork`: Not only returns the filtered set but also the rejected pile of records.
- `DistinctBy`: returns a distinct list based on an expression condition.
- `Pipe`: an all purpose extension method which gives you access to the entire collection.
- `SelectTry`: Generic method to return a different type from the select statement.

# Contribute

## Build and Test

- Run dotnet restore
- Run dotnet build
- Run dotnet test

## Installation

Use the package manager NuGet to install Dime.Linq:

`dotnet add package Dime.Linq`

## Usage

``` csharp
using System.Linq;

public void MyMethod(IEnumerable<Customer> customerList)
{
(IEnumerable<Customer> success, IEnumerable<Customer> failed)
= customerList.Fork(x => x.Address == "New York");
}
```

## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

# License

[![License](http://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://badges.mit-license.org)
# License

[MIT](https://choosealicense.com/licenses/mit/) License
Copyright Dimenics © 2020
[![License](http://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://badges.mit-license.org)
22 changes: 22 additions & 0 deletions azure-pipelines.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pool:
vmImage: windows-latest
steps:
- task: solidify-labs.chucknorris-build-enhancer.chuck-norris-task.ChuckNorrisTask@0
displayName: Quote

- task: DotNetCoreCLI@2
displayName: 'dotnet restore'
inputs:
command: restore
projects: '**/*.csproj'

- task: DotNetCoreCLI@2
displayName: 'dotnet build'
inputs:
projects: '**/*.sln'

- task: DotNetCoreCLI@2
displayName: 'dotnet test'
inputs:
command: test
projects: '**/*.csproj'
7 changes: 0 additions & 7 deletions src/Dime.Linq.sln.DotSettings.user

This file was deleted.

22 changes: 22 additions & 0 deletions src/core/AggregateExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Collections.Generic;

namespace System.Linq
{
public static class AggregateExtensions
{
/// <summary>
/// Applies an accumulator function over a sequence.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TDest">The target type</typeparam>
/// <param name="source"> An System.Collections.Generic.IEnumerable`1 to aggregate over.</param>
/// <param name="func">An accumulator function to be invoked on each element.</param>
/// <param name="separator"></param>
/// <returns> The final accumulator value.</returns>
/// <exception cref="System.ArgumentNullException">source or func is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
public static string Aggregate<TSource, TDest>(this IEnumerable<TSource> source, Func<TSource, TDest> func, string separator = ",")
=> source.Select(func).Pipe(x => string.Join(separator, x));

}
}
11 changes: 11 additions & 0 deletions src/core/ColumnComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;

namespace System.Linq
{
internal class ColumnComparer : IEqualityComparer<object[]>
{
public bool Equals(object[] x, object[] y) => x.SequenceEqual(y);

public int GetHashCode(object[] obj) => string.Join("", obj.ToArray()).GetHashCode();
}
}
15 changes: 15 additions & 0 deletions src/core/GenericExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace System.Linq
{
public static class GenericExtensions
{
/// <summary>
/// Wraps the item in a pipe
/// </summary>
/// <typeparam name="TIn">The target type</typeparam>
/// <typeparam name="TOut">The destination type</typeparam>
/// <param name="item">The item</param>
/// <param name="func">The condition</param>
/// <returns>An item of <see cref="TOut"/></returns>
public static TOut Pipe<TIn, TOut>(this TIn item, Func<TIn, TOut> func) => func(item);
}
}
14 changes: 6 additions & 8 deletions src/core/GroupExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ public static class GroupExtensions
/// <param name="elements"></param>
/// <param name="groupSelectors"></param>
/// <returns></returns>
public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(this IEnumerable<TElement> elements, params Func<TElement, object>[] groupSelectors)
{
if (groupSelectors.Length == 0)
return null;

Func<TElement, object> selector = groupSelectors.First();
return elements.GroupBy(selector);
}
public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(
this IEnumerable<TElement> elements,
params Func<TElement, object>[] groupSelectors)
=> groupSelectors.Length == 0
? null
: elements.GroupBy(item => groupSelectors.Select(sel => sel.Invoke(item)).ToArray(), new ColumnComparer());
}
}
8 changes: 4 additions & 4 deletions src/core/JoinExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public static IEnumerable<TResult> FullOuterJoin<TA, TB, TKey, TResult>(
Func<TA, TKey> selectKeyA,
Func<TB, TKey> selectKeyB,
Func<TA, TB, TKey, TResult> projection,
TA defaultA = default(TA),
TB defaultB = default(TB),
TA defaultA = default,
TB defaultB = default,
IEqualityComparer<TKey> cmp = null)
{
cmp = cmp ?? EqualityComparer<TKey>.Default;
Expand Down Expand Up @@ -88,10 +88,10 @@ public static IEnumerable<TResult> Merge<TFirst, TSecond, TResult>(
while (iterator1.MoveNext())
yield return iterator2.MoveNext() ?
operation(iterator1.Current, iterator2.Current) :
operation(iterator1.Current, default(TSecond));
operation(iterator1.Current, default);

while (iterator2.MoveNext())
yield return operation(default(TFirst), iterator2.Current);
yield return operation(default, iterator2.Current);
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions src/test/AggregateExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Linq;
using Dime.Linq.Tests.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Dime.Linq.Tests
{
[TestClass]
public class AggregateExtensionsTest
{
[TestMethod]
public void AggregateExtensions_List_JoinString_DefaultSeparator_ShouldReturnConcatenatedString()
{
List<Customer> customers = new List<Customer>()
{
new Customer(1, "Jake Marquez", "New York"),
new Customer(2, "Michael Jennings", "Pittsburgh"),
new Customer(3, "Frank Hansom", "Phoenix"),
new Customer(4, "Margareth Boyer", "New York")
};

string allCustomers = customers.Aggregate(x => x.Id);
Assert.IsTrue(allCustomers == "1,2,3,4");
}

[TestMethod]
public void AggregateExtensions_List_JoinString_CustomSeparator_ShouldReturnConcatenatedString()
{
List<Customer> customers = new List<Customer>()
{
new Customer(1, "Jake Marquez", "New York"),
new Customer(2, "Michael Jennings", "Pittsburgh"),
new Customer(3, "Frank Hansom", "Phoenix"),
new Customer(4, "Margareth Boyer", "New York")
};

string allCustomers = customers.Aggregate(x => x.Id, ". ");
Assert.IsTrue(allCustomers == "1. 2. 3. 4");
}
}
}
1 change: 1 addition & 0 deletions src/test/Dime.Linq.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFrameworks>netcoreapp3.1; net461</TargetFrameworks>

<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/test/ForkExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void Linq_Fork_WithDataInBothSets_ShouldSplitIntoTwo_PopulatedSets()
new Customer(3, "I.P. Freely", "Bumtown")
};

(IEnumerable<Customer> success, IEnumerable<Customer> failed) = customers.Fork<Customer>(x => x.Address == "Bumtown");
(IEnumerable<Customer> success, IEnumerable<Customer> failed) = customers.Fork(x => x.Address == "Bumtown");

Assert.IsTrue(success.Count() == 2);
Assert.IsTrue(failed.Count() == 1);
Expand All @@ -35,7 +35,7 @@ public void Linq_Fork_WithEmptyDataInOneSet_ShouldSplitIntoTwo_EmptyCollection()
new Customer(3, "I.P. Freely", "Bumtown")
};

(IEnumerable<Customer> success, IEnumerable<Customer> failed) = customers.Fork<Customer>(x => x.Address == "Bumtown");
(IEnumerable<Customer> success, IEnumerable<Customer> failed) = customers.Fork(x => x.Address == "Bumtown");

Assert.IsTrue(success.Count() == 2);
Assert.IsTrue(!failed.Any());
Expand All @@ -51,7 +51,7 @@ public void Linq_Fork_WithEmptyDataInBothSet_ShouldSplitIntoTwo_NoData()
new Customer(3, "I.P. Freely", "Bumtown")
};

(IEnumerable<Customer> success, IEnumerable<Customer> failed) = customers.Fork<Customer>(x => x.Address == "Not Bumtown");
(IEnumerable<Customer> success, IEnumerable<Customer> failed) = customers.Fork(x => x.Address == "Not Bumtown");

Assert.IsTrue(!success.Any());
Assert.IsTrue(failed.Count() == 3);
Expand Down
54 changes: 52 additions & 2 deletions src/test/GroupExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Dime.Linq.Tests.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Dime.Linq.Tests
{
[TestClass]
public class GroupExtensionsTests
{
[TestMethod]
public void GroupByMany_NoDuplicates_ShouldReturnFlatGroup()
{
List<Customer> customers = new List<Customer>
{
new Customer(1, "Jeff", "New York"),
new Customer(2, "Harvey", "Los Angeles"),
new Customer(3, "Donald", "New York"),
new Customer(4, "Megan", "Toronto"),
new Customer(5, "Frank", "New York"),
};

List<IGrouping<object, Customer>> groups = customers.GroupByMany(x => x.Name, x => x.Address).ToList();
Assert.IsTrue(groups.Count == 5);

Assert.IsTrue(
groups
.FirstOrDefault(x =>
{
object[] keys = x.Key as object[];
return (string)keys[0] == "Jeff" && (string)keys[1] == "New York";
})
.Count() == 1);
}

[TestMethod]
public void GroupByMany_HasDuplicates_ShouldReturnNestedGroups()
{
List<Customer> customers = new List<Customer>
{
new Customer(1, "Jeff", "New York"),
new Customer(2, "Harvey", "Los Angeles"),
new Customer(3, "Donald", "New York"),
new Customer(4, "Megan", "Toronto"),
new Customer(5, "Jeff", "New York"),
};

List<IGrouping<object, Customer>> groups = customers.GroupByMany(x => x.Name, x => x.Address).ToList();
Assert.IsTrue(groups.Count == 4);

Assert.IsTrue(
groups
.FirstOrDefault(x =>
{
object[] keys = x.Key as object[];
return (string)keys[0] == "Jeff" && (string)keys[1] == "New York";
})
.Count() == 2);
}
}
}
}
Loading