diff --git a/lib/src/list.dart b/lib/src/list.dart index d996eaf..e2feca0 100644 --- a/lib/src/list.dart +++ b/lib/src/list.dart @@ -221,3 +221,150 @@ extension ListFlattenExtension on List> { /// ``` List flatten() => [for (final list in this) ...list]; } + +extension ListMoveExtension on List { + /// Moves the [element] from its current index to the [newIndex]. + /// + /// ```dart + /// final list = [1, 2, 3]; + /// list.move(1, 2); // [2, 3, 1] + /// ``` + void move(E element, int newIndex) { + RangeError.checkValidIndex(newIndex, this, 'newIndex'); + + final currentIndex = indexOf(element); + + if (currentIndex < 0) { + return; + } + + removeAt(currentIndex); + insert(newIndex, element); + } +} + +extension ListMoveAtExtension on List { + /// Moves the element at the [oldIndex] to the [newIndex]. + /// + /// ```dart + /// final list = [1, 2, 3]; + /// list.moveAt(0, 2); // [2, 3, 1] + /// ``` + void moveAt(int oldIndex, int newIndex) { + RangeError.checkValidIndex(oldIndex, this, 'oldIndex'); + RangeError.checkValidIndex(newIndex, this, 'newIndex'); + + final element = this[oldIndex]; + + removeAt(oldIndex); + insert(newIndex, element); + } +} + +extension ListMoveAllExtension on List { + /// Moves all the elements that satisfy the given [predicate] to the [newIndex]. + /// + /// ```dart + /// final list = [1, 2, 3]; + /// list.moveAll(1, (element) => element.isOdd); // [2, 1, 3] + /// ``` + void moveAll(int newIndex, bool Function(E element) predicate) { + RangeError.checkValidIndex(newIndex, this, 'newIndex'); + + final split = partition(predicate); + + clear(); + addAll(split[1]); + insertAll(newIndex, split[0]); + } +} + +extension ListMoveUpExtension on List { + /// Moves the [element] up by one index increment unless it is at the top already. + /// + /// Returns `true` if the move was successful otherwise `false`. + /// + /// ```dart + /// final list = [1, 2, 3]; + /// list.moveUp(3); // [1, 3, 2] + /// ``` + bool moveUp(E element) { + final currentIndex = indexOf(element); + + if (currentIndex > 0) { + removeAt(currentIndex); + insert(currentIndex - 1, element); + return true; + } else { + return false; + } + } +} + +extension ListMoveUpAtExtension on List { + /// Moves the element at the [index] up by one index increment unless it is at the top already. + /// + /// Returns `true` if the move was successful otherwise `false`. + /// + /// ```dart + /// final list = [1, 2, 3]; + /// list.moveUpAt(2); // [1, 3, 2] + /// ``` + bool moveUpAt(int index) { + RangeError.checkValidIndex(index, this, 'index'); + + if (index > 0) { + final element = this[index]; + removeAt(index); + insert(index - 1, element); + return true; + } else { + return false; + } + } +} + +extension ListMoveDownExtension on List { + /// Moves the [element] down by one index increment unless it is at the bottom already. + /// + /// Returns `true` if the move was successful otherwise `false`. + /// + /// ```dart + /// final list = [1, 2, 3]; + /// list.moveDown(1); // [2, 1, 3] + /// ``` + bool moveDown(E element) { + final currentIndex = indexOf(element); + + if (currentIndex >= 0 && currentIndex < length - 1) { + removeAt(currentIndex); + insert(currentIndex + 1, element); + return true; + } else { + return false; + } + } +} + +extension ListMoveDownAtExtension on List { + /// Moves the element at the [index] down by one index increment unless it is at the bottom already. + /// + /// Returns `true` if the move was successful otherwise `false`. + /// + /// ```dart + /// final list = [1, 2, 3]; + /// list.moveDownAt(0); // [2, 1, 3] + /// ``` + bool moveDownAt(int index) { + RangeError.checkValidIndex(index, this, 'index'); + + if (index >= 0 && index < length - 1) { + final element = this[index]; + removeAt(index); + insert(index + 1, element); + return true; + } else { + return false; + } + } +} diff --git a/test/list_test.dart b/test/list_test.dart index c9ae991..fe028f7 100644 --- a/test/list_test.dart +++ b/test/list_test.dart @@ -83,5 +83,84 @@ void main() { final List flatten = nestedList.flatten(); expect(flatten, [0, 0, 0, 1, 1, 1, 2, 2, 2]); }); + + test('.move()', () { + final list = [1, 2, 3]; + expect(list..move(1, 0), [1, 2, 3]); + expect(list..move(1, 2), [2, 3, 1]); + expect(list..move(3, 0), [3, 2, 1]); + expect(() => list..move(3, 3), throwsA(isA())); + }); + + test('.moveAt()', () { + final list = [1, 2, 3]; + expect(list..moveAt(0, 0), [1, 2, 3]); + expect(list..moveAt(0, 2), [2, 3, 1]); + expect(list..moveAt(1, 0), [3, 2, 1]); + expect(() => list..moveAt(3, 0), throwsA(isA())); + expect(() => list..moveAt(0, 3), throwsA(isA())); + }); + + test('.moveAll()', () { + final list = [1, 2, 3]; + expect( + list..moveAll(1, (element) => element < 3), + [3, 1, 2], + ); + expect( + list..moveAll(1, (element) => element == 2), + [3, 2, 1], + ); + expect( + () => list..moveAll(3, (element) => true), + throwsA(isA()), + ); + }); + + test('.moveUp()', () { + final list = [1, 2, 3]; + expect(list.moveUp(0), isFalse); + expect(list, [1, 2, 3]); + expect(list.moveUp(1), isFalse); + expect(list, [1, 2, 3]); + expect(list.moveUp(2), isTrue); + expect(list, [2, 1, 3]); + expect(list.moveUp(3), isTrue); + expect(list, [2, 3, 1]); + }); + + test('.moveUpAt()', () { + final list = [1, 2, 3]; + expect(list.moveUpAt(0), isFalse); + expect(list, [1, 2, 3]); + expect(list.moveUpAt(1), isTrue); + expect(list, [2, 1, 3]); + expect(list.moveUpAt(2), isTrue); + expect(list, [2, 3, 1]); + expect(() => list.moveUpAt(3), throwsA(isA())); + }); + + test('.moveDown()', () { + final list = [1, 2, 3]; + expect(list.moveDown(0), isFalse); + expect(list, [1, 2, 3]); + expect(list.moveDown(3), isFalse); + expect(list, [1, 2, 3]); + expect(list.moveDown(2), isTrue); + expect(list, [1, 3, 2]); + expect(list.moveDown(1), isTrue); + expect(list, [3, 1, 2]); + }); + + test('.moveDownAt()', () { + final list = [1, 2, 3]; + expect(list.moveDownAt(2), isFalse); + expect(list, [1, 2, 3]); + expect(list.moveDownAt(1), isTrue); + expect(list, [1, 3, 2]); + expect(list.moveDownAt(0), isTrue); + expect(list, [3, 1, 2]); + expect(() => list.moveDownAt(3), throwsA(isA())); + }); }); }