Skip to content

Instantly share code, notes, and snippets.

@jmcd
Created October 14, 2011 15:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmcd/1287394 to your computer and use it in GitHub Desktop.
Save jmcd/1287394 to your computer and use it in GitHub Desktop.
General purpose move operation on entities with an integer rank
using System;
using System.Collections.Generic;
using System.Linq;
namespace jmcd
{
public class MoveOperation<T>
{
private readonly Func<T, int> _positionOf;
private readonly Action<T, int> _reposition;
private readonly List<T> _moved = new List<T>();
public MoveOperation(Func<T, int> positionOf, Action<T, int> reposition)
{
_positionOf = positionOf;
_reposition = reposition;
}
public IList<T> MovedItems { get { return _moved; } }
public void Execute(IList<T> collection, T subject, int offset)
{
if (offset == 0) return;
var currentIndex = _positionOf(subject);
var targetPosition = currentIndex + offset;
var siblingStrategy = offset > 0 ?
new MoveSiblingStrategy(t => _positionOf(t) > currentIndex && _positionOf(t) <= targetPosition, -1) :
new MoveSiblingStrategy(t => _positionOf(t) >= targetPosition && _positionOf(t) < currentIndex, 1);
var siblings = collection.Where(siblingStrategy.SelectorPredecate);
foreach (var sibling in siblings)
{
MoveByOffsetAndRecord(sibling, siblingStrategy.Offset);
}
MoveByOffsetAndRecord(subject, offset);
}
private class MoveSiblingStrategy
{
public readonly Func<T, bool> SelectorPredecate;
public readonly int Offset;
public MoveSiblingStrategy(Func<T, bool> selectorPredecate, int offset)
{
SelectorPredecate = selectorPredecate;
Offset = offset;
}
}
private void MoveByOffsetAndRecord(T item, int offset)
{
var newPosition = _positionOf(item);
newPosition += offset;
_reposition(item, newPosition);
_moved.Add(item);
}
}
}
using System.Collections.Generic;
using System.Linq;
using Events.Web.Core.Util;
using NUnit.Framework;
namespace jmcd
{
public class MoveOperationTestFixtures
{
public class Fruit
{
public int Position;
public string Name;
}
public class FruitContext
{
public List<Fruit> Fruits;
private IList<Fruit> _movedItems;
private class Counter
{
private int _next = 0;
public int Next()
{
var result = _next;
_next++;
return result;
}
}
public FruitContext()
{
var counter = new Counter();
Fruits = new[] {"apple", "banana", "pear"}.Select(name => new Fruit {Name = name, Position = counter.Next()}).ToList();
}
public void AssertPositions(params string[] names)
{
var orderedFruits = Fruits.OrderBy(x => x.Position);
var actualNames = orderedFruits.Select(x => x.Name).ToArray();
CollectionAssert.AreEqual(names, actualNames);
var distinctPositions = orderedFruits.Select(x => x.Position).ToList();
Assert.AreEqual(Fruits.Count, distinctPositions.Count);
}
public void Move(string name, int offset)
{
var subject = Fruits.Where(x => x.Name == name).Single();
var moveOperation = new MoveOperation<Fruit>(fruit => fruit.Position, (fruit, newPosition) => fruit.Position = newPosition);
moveOperation.Execute(Fruits, subject, offset);
_movedItems = moveOperation.MovedItems;
}
public void AssertMovedItemCount(int expected)
{
Assert.AreEqual(expected, _movedItems.Count);
}
public void AssertMoved(string name)
{
Assert.IsTrue(_movedItems.Any(x => x.Name == name));
}
}
[TestFixture]
public class InitialFruitContext
{
private readonly FruitContext _context = new FruitContext();
[Test]
public void PositionsCorrect()
{
_context.AssertPositions("apple", "banana", "pear");
}
}
[TestFixture]
public class MoveFirstDown
{
private readonly FruitContext _context = new FruitContext();
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_context.Move("apple", 1);
}
[Test]
public void PositionsCorrect()
{
_context.AssertPositions("banana", "apple", "pear");
}
[Test]
public void TwoItemsMoved()
{
_context.AssertMovedItemCount(2);
}
[Test]
public void FirstItemMoved()
{
_context.AssertMoved("apple");
}
[Test]
public void MiddleItemMoved()
{
_context.AssertMoved("banana");
}
}
[TestFixture]
public class MoveFirstUp
{
private readonly FruitContext _context = new FruitContext();
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_context.Move("apple", -1);
}
[Test]
public void PositionsCorrect()
{
_context.AssertPositions("apple", "banana", "pear");
}
[Test]
public void OneItemsMoved()
{
_context.AssertMovedItemCount(1);
}
[Test]
public void FirstItemMoved()
{
_context.AssertMoved("apple");
}
}
[TestFixture]
public class MoveMiddleDown
{
private readonly FruitContext _context = new FruitContext();
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_context.Move("banana", 1);
}
[Test]
public void PositionsCorrect()
{
_context.AssertPositions("apple", "pear", "banana");
}
[Test]
public void TwoItemsMoved()
{
_context.AssertMovedItemCount(2);
}
[Test]
public void MiddleItemMoved()
{
_context.AssertMoved("banana");
}
[Test]
public void LastItemMoved()
{
_context.AssertMoved("pear");
}
}
[TestFixture]
public class MoveMiddleUp
{
private readonly FruitContext _context = new FruitContext();
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_context.Move("banana", -1);
}
[Test]
public void PositionsCorrect()
{
_context.AssertPositions("banana", "apple", "pear");
}
[Test]
public void TwoItemsMoved()
{
_context.AssertMovedItemCount(2);
}
[Test]
public void FirstItemMoved()
{
_context.AssertMoved("apple");
}
[Test]
public void MiddleItemMoved()
{
_context.AssertMoved("banana");
}
}
[TestFixture]
public class MoveLastDown
{
private readonly FruitContext _context = new FruitContext();
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_context.Move("pear", 1);
}
[Test]
public void PositionsCorrect()
{
_context.AssertPositions("apple", "banana", "pear");
}
[Test]
public void OneItemsMoved()
{
_context.AssertMovedItemCount(1);
}
[Test]
public void LastItemMoved()
{
_context.AssertMoved("pear");
}
}
[TestFixture]
public class MoveLastUp
{
private readonly FruitContext _context = new FruitContext();
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
_context.Move("pear", -1);
}
[Test]
public void PositionsCorrect()
{
_context.AssertPositions("apple", "pear", "banana");
}
[Test]
public void TwoItemsMoved()
{
_context.AssertMovedItemCount(2);
}
[Test]
public void MiddleItemMoved()
{
_context.AssertMoved("banana");
}
[Test]
public void LastItemMoved()
{
_context.AssertMoved("pear");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment