Skip to content

Commit

Permalink
feat: introduce assoc-path and pluck
Browse files Browse the repository at this point in the history
  • Loading branch information
jackw committed Sep 3, 2020
1 parent 00eb9cb commit 644c56b
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 0 deletions.
50 changes: 50 additions & 0 deletions src/_assoc-path.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
@use './internal/is';
@import 'assoc';
@import 'has';
@import 'slice';

/// Makes a shallow clone of an object, setting or overriding the nodes required
/// to create the given path
///
/// @group object
/// @param {Array} path the path to set
/// @param {*} val The new value
/// @param {Object} obj The object to clone
/// @return {Object} A new object equivalent to the original except along the specified path.
///
/// @example scss - assoc-path
///
/// $set-prop: assoc-path('a' 'b' 'c', 42, (a: (b: (c: 0))));
/// @debug $set-prop; //=> (a: (b: (c: 42)))

@function assoc-path($path, $val, $obj) {
@if length($path) == 0 {
@return $val;
}

$idx: nth($path, 1);

@if length($path) > 1 {
$next-obj: if(
is.map($obj) and has($idx, $obj),
map-get($obj, $idx),
()
);
$val: assoc-path(slice(2, length($path), $path), $val, $next-obj);
}

$result: ();

@if is.number($idx) and is.list($obj) {
@if length($obj) == 0 {
$result: append($obj, $val);
} @else {
$result: set-nth($obj, $idx, $val);
}
} @else {
$_obj: if(is.null($obj), (), $obj);
$result: assoc($idx, $val, $_obj);
}

@return $result;
}
21 changes: 21 additions & 0 deletions src/_pluck.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@import 'prop';
@import 'map';
@import 'last';

/// Returns a new list by plucking the same named property from all objects in
/// the supplied list.
///
/// @group list
/// @param {Number|String} key The key name to pluck from each object.
/// @param {Array} arr The array to consider.
/// @return {Array} The list of values for the given key.
/// @see props
///
/// @example scss - pluck
///
/// $avenger-names: pluck('name', $avengers)
/// @debug $avenger-names; //=> ('Black Widow' 'Captain America' 'Hawkeye' 'Hulk' 'Iron man' 'Thor')

@function pluck($key, $arr) {
@return map((prop, $key), $arr);
}
2 changes: 2 additions & 0 deletions src/_sass-fire.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@import 'all-pass';
@import 'any-pass';
@import 'aperture';
@import 'assoc-path';
@import 'assoc';
@import 'compose';
@import 'concat';
Expand Down Expand Up @@ -53,6 +54,7 @@
@import 'paths';
@import 'pick';
@import 'pipe';
@import 'pluck';
@import 'prepend';
@import 'product';
@import 'prop-eq';
Expand Down
109 changes: 109 additions & 0 deletions test/_assoc-path.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
@import 'true';
@import '../src/assoc-path';
@import '../src/path';

@include describe('assoc-path [function]') {
@include it('overrides only what is necessary for the path') {
$obj1: (
a: (
b: 1,
c: 2,
d: (
e: 3,
),
),
f: (
g: (
h: 4,
i: (
5,
6,
7,
),
j: (
k: 6,
l: 7,
),
),
),
m: 8,
);
$obj2: assoc-path(('f', 'g', 'i', 2), 42, $obj1);
@include assert-equal(path(f g i, $obj2), (5, 42, 7));
@include assert-equal(path(a, $obj2), path(a, $obj1));
@include assert-equal(path(m, $obj2), path(m, $obj1));
@include assert-equal(path(f g h, $obj2), path(f g h, $obj1));
@include assert-equal(path(f g j, $obj2), path(f g j, $obj1));
}

@include it(
'is the equivalent of clone and setPath if the property is not on the original'
) {
$obj1: (
a: 1,
b: (
c: 2,
d: 3,
),
e: 4,
f: 5,
);
$obj2: assoc-path(('x', 1, 'y'), 42, $obj1);
@include assert-equal(
$obj2,
(
a: 1,
b: (
c: 2,
d: 3,
),
e: 4,
f: 5,
x:
append(
(),
(
y: 42,
)
),
)
);
@include assert-equal(path(a, $obj2), path(a, $obj1));
@include assert-equal(path(b, $obj2), path(b, $obj1));
@include assert-equal(path(e, $obj2), path(e, $obj1));
@include assert-equal(path(f, $obj2), path(f, $obj1));
}

@include it('empty path replaces the whole object') {
@include assert-equal(
assoc-path(
(),
3,
(
a: 1,
b: 2,
)
),
3
);
}

@include it('replaces `null` with a new object') {
@include assert-equal(
assoc-path(
('foo', 'bar', 'baz'),
42,
(
foo: null,
)
),
(
foo: (
bar: (
baz: 42,
),
),
)
);
}
}
38 changes: 38 additions & 0 deletions test/_pluck.spec.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@import 'true';
@import '../src/pluck';

@include describe('pluck [function]') {
$the-avengers: (
(
name: 'Black Widow',
age: 34,
),
(
name: 'Captain America',
age: 100,
),
(
name: 'Hawkeye',
age: 47,
),
(
name: 'Hulk',
age: 49,
),
(
name: 'Iron man',
age: '48',
),
(
name: 'Thor',
age: 'unknown',
)
);

@include it('should appropriate property over an array') {
@include assert-equal(
pluck('name', $the-avengers),
('Black Widow' 'Captain America' 'Hawkeye' 'Hulk' 'Iron man' 'Thor')
);
}
}

0 comments on commit 644c56b

Please sign in to comment.