Skip to content

Commit

Permalink
feat(path): add paths, path, path-or, path-eq
Browse files Browse the repository at this point in the history
  • Loading branch information
jackw committed Aug 24, 2020
1 parent 6d8054c commit 0c88c44
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/_path-eq.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@import 'path';
@import 'equals';

/// Determines whether a nested path on an object has a specific value.
/// Most likely used to filter a list.
///
/// @group object
/// @param {List} path The path to use.
/// @param {*} value The value to compare.
/// @param {Object} map The map to retrieve the nested property from.
/// @return {*} The data at `path` of the supplied object or the default value.
///
/// @example scss - path-eq
///
/// $is-42: path-eq(('x', 'y'), 42, (x: (y: 42)));
/// @debug $is-42; //=> true

@function path-eq($path, $value, $map) {
@return equals(path($path, $map), $value);
}
20 changes: 20 additions & 0 deletions src/_path-or.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@import 'path';

/// If the given, non-null object has a value at the given path, returns the
/// value at that path. Otherwise returns the provided default value.
///
/// @group object
/// @param {*} d The default value.
/// @param {List} path The path to use.
/// @param {Object} map The map to retrieve the nested property from.
/// @return {*} The data at `path` of the supplied object or the default value.
///
/// @example scss - path-or
///
/// $get-default: path-or('N/A', ('x', 'y'), (a: (b: 2)));
/// @debug $get-value; //=> "N/A"

@function path-or($d, $path, $map) {
$value: path($path, $map);
@return if($value, $value, $d);
}
18 changes: 18 additions & 0 deletions src/_path.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@import 'head';
@import 'paths';

/// Retrieves the value at a given path of an object
///
/// @group object
/// @param {List} path The path to use.
/// @param {Object} map The map to retrieve the nested property from.
/// @return {*} The data at `path` of the supplied object or null.
///
/// @example scss - path
///
/// $get-value: path(('a', 'b'), (a: (b: 2)));
/// @debug $get-value; //=> 2

@function path($path, $map) {
@return head(paths($path, $map));
}
58 changes: 58 additions & 0 deletions src/_paths.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
@import 'inc';
@import 'init';
@import 'last';
@import 'map';

$_sass-fire-paths-map: () !global;

/// Retrieves the values at a given path of an object
///
/// @group object
/// @param {List} paths-array The array of paths to be fetched.
/// @param {Object} map The map to retrieve the nested properties from.
/// @return {*} A list consisting of values at paths specified by "paths-array".
///
/// @example scss - paths
///
/// $get-values: paths(('a', 'b'), ('p', 0, 'q'), (a: (b: 2), p: ((q: 3))));
/// @debug $get-values; //=> (2, 3)

@function paths($args...) {
$map: last($args);
$paths: init($args);
$_sass-fire-paths-map: $map !global;

@return map(_sass-fire-path, $paths);
}

@function _sass-fire-path($path) {
$val: $_sass-fire-paths-map;
$idx: 1;
$p: null;

@while ($idx <= length($path)) {
@if $val == null {
@return null;
}
$p: nth($path, $idx);
@if type-of($val) == 'map' {
$val: if(map-has-key($val, $p), map-get($val, $p), null);
} @else if
type-of($val) ==
'list' and
type-of($p) ==
'number' and
$p !=
0 and
abs($p) <=
length($val)
{
$val: nth($val, $p);
} @else {
$val: null;
}
$idx: inc($idx);
}

@return $val;
}
46 changes: 46 additions & 0 deletions test/_path-eq.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@import 'true';
@import '../src/path-eq';

@include describe('path-eq [function]') {
$input: (
a: 1,
b: (
(
ba: 2,
),
(
ba: 3,
),
),
);

@include it('returns true if the path matches the value') {
@include assert-equal(path-eq(('a'), 1, $input), true);
@include assert-equal(path-eq(('b', 2, 'ba'), 3, $input), true);
}

@include it('returns false for non matches') {
@include assert-equal(path-eq(('a'), '1', $input), false);
@include assert-equal(path-eq(('b', 1, 'ba'), 3, $input), false);
}

@include it('returns false for non existing values') {
@include assert-equal(path-eq(('c'), 'foo', $input), false);
@include assert-equal(path-eq(('c', 'd'), 'foo', $input), false);
}

@include it('accepts empty path') {
@include assert-equal(
path-eq(
(),
42,
(
a: 1,
b: 2,
)
),
false
);
@include assert-equal(path-eq((), obj, obj), true);
}
}
40 changes: 40 additions & 0 deletions test/_path-or.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@import 'true';
@import '../src/path-or';

@include describe('path-or [function]') {
@include it(
'takes a path and an object and returns the value at the path or the default value'
) {
$input: (
a: (
b: (
c: 100,
d: 200,
),
e: (
f: (
100,
101,
102,
),
g: 'G',
),
h: 'H',
),
i: 'I',
j: join((), 'J'),
);
@include assert-equal(path-or('Unknown', ('a', 'b', 'c'), $input), 100);
@include assert-equal(path-or('Unknown', (), $input), $input);
@include assert-equal(
path-or('Unknown', ('a', 'e', 'f', 2), $input),
101
);
@include assert-equal(path-or('Unknown', ('j', 1), $input), 'J');
@include assert-equal(path-or('Unknown', ('j', 2), $input), 'Unknown');
@include assert-equal(
path-or('Unknown', ('a', 'b', 'c'), null),
'Unknown'
);
}
}
59 changes: 59 additions & 0 deletions test/_path.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@import 'true';
@import '../src/path';

@include describe('path [function]') {
@include it('should return values in objects') {
@include assert-equal(
path(
('a', 'b'),
(
a: (
b: 2,
),
)
),
2
);
@include assert-equal(
path(
('a', 'b'),
(
c: (
b: 2,
),
)
),
null
);
@include assert-equal(
path(
('a', 'b', 1),
(
a: (
b: (
1,
2,
3,
),
),
)
),
1
);
@include assert-equal(
path(
('a', 'b', -2),
(
a: (
b: (
1,
2,
3,
),
),
)
),
2
);
}
}
72 changes: 72 additions & 0 deletions test/_paths.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@import 'true';
@import '../src/paths';

@include describe('paths [function]') {
$input: (
a: (
b: (
c: 1,
d: 2,
),
),
p: (
(
q: 3,
),
'Hi',
),
x: (
y: 'Alice',
z: (
(append((), ())),
),
),
);

@include it('takes paths and returns values at those paths') {
@include assert-equal(
paths(('a' 'b' 'c'), ('x' 'y'), $input),
(1 'Alice')
);
@include assert-equal(
paths(('a', 'b', 'd'), ('p', 'q'), $input),
(2 null)
);
}
@include it('takes a paths that contains indices into arrays') {
@include assert-equal(
paths(('p', 1, 'q'), ('x', 'z', 1, 1), $input),
(3 ())
);
@include assert-equal(
paths(('p', 1, 'q'), ('x', 'z', 3, 2), $input),
(3 null)
);
}
@include it('takes a path that contains negative indices into arrays') {
@include assert-equal(
paths(('p', -2, 'q'), ('p', -1), $input),
(3 'Hi')
);
@include assert-equal(
paths(('p', -4, 'q'), ('x', 'z', -1, 1), $input),
(null ())
);
}

@include it("gets a deep property's value from objects") {
@include assert-equal(
paths(('a' 'b'), $input),
append((), map-get(map-get($input, 'a'), 'b'))
);
@include assert-equal(
paths(('p' 1), $input),
append((), nth(map-get($input, 'p'), 1))
);
}

@include it('returns null for items not found') {
@include assert-equal(paths(('a' 'x' 'y'), $input), append((), null));
@include assert-equal(paths(('p' 3), $input), append((), null));
}
}

0 comments on commit 0c88c44

Please sign in to comment.