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 Where(), Any(), First(), FirstOrDefault(), Single() and SingleOrDefault() #76

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018-2021 Antao Almada
Copyright (c) 2018-2023 Antao Almada

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
Expand All @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.8" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NetFabric.Assertive" Version="3.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="NetFabric.Assertive" Version="4.0.0" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="3.0.3">
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
121 changes: 117 additions & 4 deletions NetFabric.DoublyLinkedList/DoublyLinkedList'1.ForwardEnumeration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;

Expand All @@ -18,6 +17,120 @@ public readonly struct ForwardEnumeration
internal ForwardEnumeration(DoublyLinkedList<T> list)
=> this.list = list;

public WhereForwardEnumeration Where(Func<T, bool> predicate)
=> new(list, predicate);

public bool Any()
=> !list.IsEmpty;

public bool Any(Func<T, bool> predicate)
{
var current = list.First;
while (current is not null)
{
if (predicate(current.Value))
return true;
current = current.Next;
}
return false;
}

public T First()
{
if (list.IsEmpty)
Throw.InvalidOperationException(Resources.NoElements);
return list.First!.Value;
}

public T First(Func<T, bool> predicate)
{
var current = list.First;
while (current is not null)
{
if (predicate(current.Value))
return current.Value;
current = current.Next;
}
return Throw.InvalidOperationException<T>(Resources.NoMatches);
}

public T? FirstOrDefault()
=> list.First is not null
? list.First.Value
: default;

public T? FirstOrDefault(Func<T, bool> predicate)
{
var current = list.First;
while (current is not null)
{
if (predicate(current.Value))
return current.Value;
current = current.Next;
}
return default;
}

public T Single()
{
if (list.IsEmpty)
Throw.InvalidOperationException(Resources.NoElements);
if (list.First != list.Last)
Throw.InvalidOperationException(Resources.MoreThanOneElement);
return list.First!.Value;
}

public T Single(Func<T, bool> predicate)
{
var current = list.First;
while (current is not null)
{
if (predicate(current.Value))
{
var value = current.Value;

// found first, keep going until end or find second
current = current.Next;
while (current is not null)
{
if (predicate(current.Value))
Throw.InvalidOperationException(Resources.MoreThanOneMatch);
}

return value;
}
}
return Throw.InvalidOperationException<T>(Resources.NoMatches);
}

public T? SingleOrDefault()
=> list.First is not null
? Single()
: default;

public T? SingleOrDefault(Func<T, bool> predicate)
{
var current = list.First;
while (current is not null)
{
if (predicate(current.Value))
{
var value = current.Value;

// found first, keep going until end or find second
current = current.Next;
while (current is not null)
{
if (predicate(current.Value))
Throw.InvalidOperationException(Resources.MoreThanOneMatch);
}

return value;
}
}
return default;
}

public int Count =>
list.count;

Expand Down Expand Up @@ -102,12 +215,12 @@ public bool MoveNext()
return current is not null;
}

public readonly void Reset()
=> Throw.NotSupportedException();
public void Reset()
=> current = null;

public readonly void Dispose()
{ }
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public readonly struct ReverseEnumeration
internal ReverseEnumeration(DoublyLinkedList<T> list)
=> this.list = list;

public WhereReverseEnumeration Where(Func<T, bool> predicate)
=> new(list, predicate);

public int Count =>
list.count;

Expand Down Expand Up @@ -101,8 +104,8 @@ public bool MoveNext()
return current is not null;
}

public readonly void Reset()
=> Throw.NotSupportedException();
public void Reset()
=> current = null;

public readonly void Dispose()
{ }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using NetFabric.Hyperlinq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;

namespace NetFabric
{
public partial class DoublyLinkedList<T>
{
public readonly struct WhereForwardEnumeration
: IValueEnumerable<T, WhereForwardEnumeration.DisposableEnumerator>
{
readonly DoublyLinkedList<T> list;
readonly Func<T, bool> predicate;

internal WhereForwardEnumeration(DoublyLinkedList<T> list, Func<T, bool> predicate)
{
this.list = list;
this.predicate = predicate;
}

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() =>
new(list, predicate);

DisposableEnumerator IValueEnumerable<T, WhereForwardEnumeration.DisposableEnumerator>.GetEnumerator() =>
new(list, predicate);

IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
new DisposableEnumerator(list, predicate);

IEnumerator IEnumerable.GetEnumerator() =>
new DisposableEnumerator(list, predicate);

public struct Enumerator
{
readonly DoublyLinkedList<T> list;
readonly Func<T, bool> predicate;
readonly int version;
Node? current;

internal Enumerator(DoublyLinkedList<T> list, Func<T, bool> predicate)
{
this.list = list;
this.predicate = predicate;
version = list.version;
current = null;
}

public readonly T Current
=> current!.Value;

public bool MoveNext()
{
if (version != list.version)
Throw.InvalidOperationException();
do
{
current = current is null
? list.First
: current.Next;
}
while (current is not null && !predicate(current.Value));
return current is not null;
}
}

public struct DisposableEnumerator
: IEnumerator<T>
{
readonly DoublyLinkedList<T> list;
readonly Func<T, bool> predicate;
readonly int version;
Node? current;

internal DisposableEnumerator(DoublyLinkedList<T> list, Func<T, bool> predicate)
{
this.list = list;
this.predicate = predicate;
version = list.version;
current = null;
}

public readonly T Current
=> current!.Value;
readonly object? IEnumerator.Current
=> current!.Value;

public bool MoveNext()
{
if (version != list.version)
Throw.InvalidOperationException();
do
{
current = current is null
? list.First
: current.Next;
}
while (current is not null && !predicate(current.Value));
return current is not null;
}

public void Reset()
=> current = null;

public readonly void Dispose()
{ }
}
}
}
}
Loading
Loading