Skip to content

Commit

Permalink
feat(list): introduce aperture, find, includes, prepend, split-every
Browse files Browse the repository at this point in the history
  • Loading branch information
jackw committed Aug 27, 2020
1 parent df76ba8 commit 7d23f74
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/_aperture.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@use 'sass:math';
@import 'slice';
@import 'inc';

/// Returns a new list, composed of n-tuples of consecutive elements. If `n` is
/// greater than the length of the list, an empty list is returned.
///
/// @group list
/// @param {Number} n The size of the tuples to create
/// @param {Array} list The list to split into `n`-length tuples
/// @return {Array} The resulting list of `n`-length tuples
///
/// @example scss - aperture
///
/// $consecutive: aperture(2, (1, 2, 3, 4, 5);
/// @debug $consecutive; //=> (1 2) (2 3) (3 4) (4 5)

@function aperture($n, $list) {
@if ($n > length($list)) {
@return ();
}
$result: ();
$idx: 1;
$step: $n - 1;
$list-length: length($list);
$slice-to: if($n > $list-length, $list-length, $n);
@while $idx <= $list-length {
@if $idx > $list-length - $step {
@return $result;
}
$slice: slice($idx, $slice-to, $list);
$result: append($result, $slice);
$idx: $idx + 1;
$slice-to: math.clamp($idx, $idx + $step, $list-length);
}
@return $result;
}
31 changes: 31 additions & 0 deletions src/_find.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@import 'inc';
@import './internal/run-function-with-params';

/// Returns the first element of the list which matches the predicate, or
/// `null` if no element matches.
///
/// @group list
/// @param {Function} pred The predicate function used to determine if the element is the desired one.
/// @param {Array} list The array to consider.
/// @return {*} The element found, or `null`.
/// @see reject
///
/// @example scss - find
///
/// @function isEven($n) {
/// @return $n % 2 == 0;
/// }
/// $find-first-even: find(isEven, (1, 2, 3, 4));
/// @debug $find-first-even; //=> (2 4)

@function find($pred, $list) {
$idx: 1;
@while $idx <= length($list) {
$result: call(get-function($pred), nth($list, $idx));
@if $result {
@return nth($list, $idx);
}
$idx: inc($idx);
}
@return null;
}
34 changes: 34 additions & 0 deletions src/_includes.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@import 'equals';
@import 'inc';

/// Returns `true` if the specified value is equal, in [`equals`](#equals)
/// terms, to at least one element of the given list; `false` otherwise.
/// Also works with strings.
///
/// @group list
/// @param {*} a The item to compare against.
/// @param {Array|String} list The array to consider.
/// @return {Boolean} `true` if an equivalent item is in the list, `false` otherwise.
/// @see dec
///
/// @example scss - includes
///
/// $has-seventeen: includes(17, (1, 5, 6, 11, 17, 28));
/// @debug $has-seventeen; //=> true

@function includes($a, $list) {
$idx: 1;

@if (type-of($list) == 'string') {
// not the prettiest but probably best to return truthy.
@return if(str-index($list, $a), true, false);
}

@while $idx <= length($list) {
@if (equals(nth($list, $idx), $a)) {
@return true;
}
$idx: inc($idx);
}
@return false;
}
19 changes: 19 additions & 0 deletions src/_prepend.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@import 'concat';

///
/// Returns a new list with the given element at the front, followed by the
/// contents of the list.
///
/// @group list
/// @param {*} el The item to add to the head of the output list.
/// @param {Array} list The array to add to the tail of the output list.
/// @return {Array} A new array.
///
/// @example scss - prepend
///
/// R.prepend('fee', ('fi', 'fo', 'fum')); //=> ('fee', 'fi', 'fo', 'fum')
////

@function prepend($el, $list) {
@return concat(append((), $el), $list);
}
39 changes: 39 additions & 0 deletions src/_split-every.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@use 'sass:math';
@import 'slice';
@import 'inc';

/// Splits a collection into slices of the specified length.
///
/// @group list
/// @param {Number} n
/// @param {Array} list
/// @return {Array}
/// @see implode
///
/// @example scss - split-every
///
/// $splitted: split-every(2, (1, 2, 3, 4));
/// @debug $splitted; //=> (1 2) (3 4);

@function split-every($n, $list) {
@if $n <= 0 {
@error "First argument to split-every must be a positive integer";
}
$result: ();
$idx: 1;
$list-length: if(
type-of($list) == 'string',
str-length($list),
length($list)
);
$slice-to: if($n > $list-length, $list-length, $n);

@while $idx <= $list-length {
$slice: slice($idx, $slice-to, $list);
$result: append($result, $slice);
$idx: $slice-to + 1;
$slice-to: math.clamp($idx, $slice-to + $n, $list-length);
}

@return $result;
}
33 changes: 33 additions & 0 deletions test/_aperture.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@import 'true';
@import '../src/aperture';

@include describe('aperture [function]') {
@include it('creates a list of n-tuples from a list') {
$seven-ls: (1, 2, 3, 4, 5, 6, 7);
@include assert-equal(
aperture(1, $seven-ls),
(1) (2) (3) (4) (5) (6) (7),
$inspect: true
);
@include assert-equal(
aperture(2, $seven-ls),
(1 2) (2 3) (3 4) (4 5) (5 6) (6 7),
$inspect: true
);
@include assert-equal(
aperture(3, $seven-ls),
(1 2 3) (2 3 4) (3 4 5) (4 5 6) (5 6 7),
$inspect: true
);
@include assert-equal(
aperture(4, (1, 2, 3, 4)),
append((), (1 2 3 4)),
$inspect: true
);
}

@include it('returns an empty list when `n` > `list.length`') {
@include assert-equal(aperture(6, (1, 2, 3)), ());
@include assert-equal(aperture(1, ()), ());
}
}
44 changes: 44 additions & 0 deletions test/_find.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@import 'true';
@import '../src/find';

@function _find_spec_isEven($n) {
@if type-of($n) == 'number' {
@return $n % 2 == 0;
}
@return false;
}
@function _find_spec_gt-100($n) {
@if type-of($n) == 'number' {
@return $n > 100;
}
@return false;
}
@function _find_spec_is-string($str) {
@return type-of($str) == 'string';
}
@function _find_spec_x-gt-100($o) {
@if type-of($o) == 'map' {
@return map-get($o, 'x') > 100;
}
@return false;
}

@include describe('find [function]') {
$obj: (
x: 200,
);
$a: [11, 10, 9, 'cow', $obj, 8, 7, 100, 200, 300, 4, 3, 2, 1, 0];

@include it('returns the first element that satisfies the predicate') {
@include assert-equal(find(_find_spec_isEven, $a), 10);
@include assert-equal(find(_find_spec_gt-100, $a), 200);
@include assert-equal(find(_find_spec_is-string, $a), 'cow');
@include assert-equal(find(_find_spec_x-gt-100, $a), $obj);
}
@include it('returns `null` when no element satisfies the predicate') {
@include assert-equal(find(_find_spec_isEven, ('zing')), null);
}
@include it('returns `null` when given an empty list') {
@include assert-equal(find(_find_spec_isEven, ()), null);
}
}
29 changes: 29 additions & 0 deletions test/_includes.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@import 'true';
@import '../src/includes';

@include describe('includes [function]') {
@include it('returns true if an element is in a list') {
@include assert-true(includes(7, (1, 2, 3, 9, 8, 7, 100, 200, 300)));
@include assert-true(
includes(
(
name: 'Thor',
),
((name: 'Thor'), (name: 'Iron Man'), (name: 'Black Widow'))
)
);
}

@include it('returns false if an element is not in a list') {
@include assert-false(includes(99, (1, 2, 3, 9, 8, 7, 100, 200, 300)));
}

@include it('returns false for the empty list') {
@include assert-false(includes(1, ()));
}

@include it('returns true if substring is part of string') {
@include assert-true(includes('ba', 'banana'));
@include assert-false(includes('no thanks', 'yes please'));
}
}
16 changes: 16 additions & 0 deletions test/_prepend.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import 'true';
@import '../src/prepend';

@include describe('prepend [function]') {
@include it('adds the element to the beginning of the list') {
@include assert-equal(prepend('x', ('y', 'z')), ('x' 'y' 'z'));
@include assert-equal(
prepend(('a', 'z'), ('x', 'y')),
('a', 'z') 'x' 'y'
);
}

@include it('works on empty list') {
@include assert-equal(prepend(1, ()), (1), $inspect: true);
}
}
39 changes: 39 additions & 0 deletions test/_split-every.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@import 'true';
@import '../src/split-every';

@include describe('split-every [function]') {
@include it('splits a collection into slices of the specified length') {
@include assert-equal(
split-every(1, (1, 2, 3, 4)),
(1) (2) (3) (4),
$inspect: true
);
@include assert-equal(
split-every(2, (1, 2, 3, 4)),
(1 2) (3 4),
$inspect: true
);
@include assert-equal(
split-every(3, (1, 2, 3, 4)),
(1 2 3) (4),
$inspect: true
);
@include assert-equal(
split-every(4, (1, 2, 3, 4)),
append((), (1 2 3 4)),
$inspect: true
);
@include assert-equal(
split-every(5, (1, 2, 3, 4)),
append((), (1 2 3 4)),
$inspect: true
);
@include assert-equal(split-every(3, ()), ());
@include assert-equal(split-every(1, 'abcd'), ('a' 'b' 'c' 'd'));
@include assert-equal(split-every(2, 'abcd'), ('ab') ('cd'));
@include assert-equal(split-every(3, 'abcd'), ('abc') ('d'));
@include assert-equal(split-every(4, 'abcd'), append((), ('abcd')));
@include assert-equal(split-every(5, 'abcd'), append((), ('abcd')));
@include assert-equal(split-every(3, ''), ());
}
}

0 comments on commit 7d23f74

Please sign in to comment.