Skip to content

Commit

Permalink
Added IList<T>.MutateToBeLike(source) extension method
Browse files Browse the repository at this point in the history
  • Loading branch information
lassevk committed May 1, 2017
1 parent cdc5fa8 commit c6e7893
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 0 deletions.
2 changes: 2 additions & 0 deletions DiffLib.Tests/DiffLib.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<Compile Include="DiffTests.cs" />
<Compile Include="ElementSimilarityDiffElementAlignerTests.cs" />
<Compile Include="Issues\Issue9.cs" />
<Compile Include="ListExtensionsTests.cs" />
<Compile Include="MergeTests.cs" />
<Compile Include="OptionTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand All @@ -79,6 +80,7 @@
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Runtime" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DiffLib\DiffLib.csproj">
Expand Down
47 changes: 47 additions & 0 deletions DiffLib.Tests/ListExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Linq;

using NUnit.Framework;

// ReSharper disable PossibleNullReferenceException
// ReSharper disable InvokeAsExtensionMethod
// ReSharper disable AssignNullToNotNullAttribute

namespace DiffLib.Tests
{
[TestFixture]
public class ListExtensionsTests
{
[Test]
public void Mutate_NullTarget_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => ListExtensions.MutateToBeLike(null, new int[0]));
}

[Test]
public void Mutate_NullSource_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => ListExtensions.MutateToBeLike(new int[0], null));
}

[Test]
[TestCase("123456789", "123456789")]
[TestCase("123456789", "12456789")]
[TestCase("123456789", "1234x5a6789")]
[TestCase("123456789", "1234556789")]
[TestCase("123456789", "")]
[TestCase("", "12456789")]
[TestCase("123456789", "0")]
[TestCase("123456789", "----------------------")]
[TestCase("123456789", "987654321")]
public void Mutate_TestCases(string target, string source)
{
var targetChars = target.ToCharArray().ToList();
var sourceChars = source.ToCharArray();

targetChars.MutateToBeLike(sourceChars);

Assert.That(new string(targetChars.ToArray()), Is.EqualTo(source));
}
}
}
91 changes: 91 additions & 0 deletions DiffLib/ListExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;

using JetBrains.Annotations;

namespace DiffLib
{
/// <summary>
/// This class adds extension methods for <see cref="IList{T}"/>.
/// </summary>
[PublicAPI]
public static class ListExtensions
{
/// <summary>
/// Mutate the specified list to have the same elements as another list, by inserting or removing as needed. The end result is that
/// <paramref name="target"/> will have equivalent elements as <paramref name="source"/>, in the same order and positions.
/// </summary>
/// <typeparam name="T">
/// The type of elements in the lists.
/// </typeparam>
/// <param name="target">
/// The list to mutate. Elements will possibly be inserted into or deleted from this list.
/// </param>
/// <param name="source">
/// The list to use as the source of mutations for <paramref name="target"/>.
/// </param>
/// <param name="comparer">
/// The optional <see cref="IEqualityComparer{T}"/> to use when comparing elements.
/// If not specified/<c>null</c>, <see cref="EqualityComparer{T}.Default"/> will be used.
/// </param>
/// <param name="aligner">
/// The <see cref="IDiffElementAligner{T}"/> to use when aligning elements.
/// If not specified/<c>null</c>, <see cref="BasicReplaceInsertDeleteDiffElementAligner{T}"/> will be used.
/// </param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="target"/> is <c>null</c>.</para>
/// <para>- or -</para>
/// <para><paramref name="source"/> is <c>null</c>.</para>
/// </exception>
/// <remarks>
/// The main purpose of this method is to avoid clearing and refilling the list from scratch and instead
/// make adjustments to it to have the right elements. Useful in conjunction with UI bindings and
/// similar that react to changes to the list.
/// </remarks>
public static void MutateToBeLike<T>([NotNull] this IList<T> target, [NotNull] IList<T> source, [CanBeNull] IEqualityComparer<T> comparer = null, [CanBeNull] IDiffElementAligner<T> aligner = null)
{
if (target == null)
throw new ArgumentNullException(nameof(target));
if (source == null)
throw new ArgumentNullException(nameof(source));

comparer = comparer ?? EqualityComparer<T>.Default;
aligner = aligner ?? new BasicReplaceInsertDeleteDiffElementAligner<T>();

var sections = Diff.CalculateSections(target, source, comparer);
var items = Diff.AlignElements(target, source, sections, aligner).ToList();

Assume.That(items != null);

int targetIndex = 0;
foreach (var item in items)
{
switch (item.Operation)
{
case DiffOperation.Match:
targetIndex++;
break;

case DiffOperation.Insert:
target.Insert(targetIndex, item.ElementFromCollection2.Value);
targetIndex++;
break;

case DiffOperation.Delete:
target.RemoveAt(targetIndex);
break;

case DiffOperation.Replace:
case DiffOperation.Modify:
target[targetIndex] = item.ElementFromCollection2.Value;
targetIndex++;
break;

default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}

0 comments on commit c6e7893

Please sign in to comment.