Skip to content

Commit

Permalink
add without(...sl,...str) method
Browse files Browse the repository at this point in the history
  • Loading branch information
rawpixel-vincent committed Mar 5, 2024
1 parent eace4ea commit 3eba0ee
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 6 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ The StringList class extends the Array interface types to work with string liter
- an `enum` property is available to access the string literals. It's a frozen object built from the array values, it's used as a lookup instead of the array, but the primary intent is for convenience. This construct is not meant to manipulate millions of strings.

- then additional methods for string literals and type constructs are implemented:
- `without(...$)`: opposite of concat, accept string / list parameters.
- `withPrefix($)`: add prefix to all the words.
- `withSuffix($)`: add suffix to all the words.
- `withDerivatedPrefix($)` and `withDerivatedSuffix($)`: Generate words variants with or without the given suffix/prefix depending on their presence.
- `value($)`: similar to enum but throws an error if the value doesn't exists.
- `enum` Object is exposed as readonly.
- `enum[$]:$` Object is exposed as readonly.
- `withTrim()`: trim all the words.
- `withReplace(search, replacement)`: call the String.prototype.replace on all the words.
- `withReplaceAll(search, replacement)`: call the String.prototype.replaceAll on all the words.
Expand Down Expand Up @@ -78,6 +79,8 @@ v.withDerivatedPrefix('#') => SL<"foo" | "#foo" | "bar" | "#bar">;
v.withTrim() => SL<"foo" | "bar">;
v.withReplace('a', 'e') => SL<"foo" | "ber">;
v.withReplaceAll('o', 'e') => SL<"fee" | "bar">;

v.without('foo') => SL<"bar">;
```

```js
Expand All @@ -102,6 +105,9 @@ const suffixed = list.withSuffix('.suffix');
const concat = prefixed.concat(...suffixed);
// SL<"prefix.foo" | "prefix.bar" | "foo.suffix" | "bar.suffix">;

const without = concat.without('prefix.foo').without('bar.suffix');
// SL<"prefix.bar" | "foo.suffix">;

const bothWay = list.withPrefix('data.').withSuffix('.ext');
// SL<"data.foo.ext" | "data.bar.ext">;

Expand Down
2 changes: 1 addition & 1 deletion StringLiteralList.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ export interface IStringList<T extends string>
mutable(): T & string[];
sort<P1 = T, P2 = T>(compareFn?: (a: P1, b: P2) => number): this;
reverse(): this;
without<PP extends T & string, S extends string>(...arg: (ILiterals<S> | S)[]): IStringList<Exclude<PP | S, S>>;

// Implemented methods to return the frozen array, typed as IStringList.
toSorted(compareFn?: (a: T, b: T) => number): IStringList<T>;
toReversed(): IStringList<T>;

concat<PP extends T, S extends string>(...arg: (ILiterals<S> | S)[]): IStringList<PP | S>;

// Readonly overrides
readonly length: number;
readonly [n: number]: T | undefined;
Expand Down
11 changes: 11 additions & 0 deletions StringLiteralList.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ export class SL extends Array {
return Object.freeze(new SL(...super.slice.apply(this, arguments)));
}

without() {
const values = Array.from(arguments).flatMap((el) =>
Array.isArray(el)
? el.filter((s) => typeof s === 'string')
: typeof el === 'string'
? [el]
: [],
);
return Object.freeze(new SL(...this.filter((e) => !values.includes(e))));
}

withTrim() {
return Object.freeze(new SL(...super.map((e) => e.trim())));
}
Expand Down
11 changes: 7 additions & 4 deletions stringList.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,27 @@ import { SL } from './StringLiteralList.js';
/** @type {import('./stringList.js').stringList} */
export function stringList(...strings) {
let values = strings;
if (strings.length && strings.some((el) => typeof el !== 'string')) {
let invalid = strings.some((el) => typeof el !== 'string');
if (strings.length && invalid) {
/* c8 ignore start */
if (
typeof window === 'undefined' &&
process?.env?.NODE_ENV !== 'production' &&
process?.env?.NODE_ENV !== 'test'
) {
console.debug(
'Unexpected type in stringList(). Casting all arguments to string type.',
`Unexpected type in stringList(${typeof invalid}). Casting all arguments to string type.`,
);
}
/* c8 ignore stop */
values = strings.flatMap((el) =>
Array.isArray(el)
? el.filter((s) => typeof s === 'string').map((s) => s)
? el.filter((s) => typeof s === 'string')
: typeof el === 'string'
? [el]
: [],
: typeof el === 'number'
? [String(el)]
: [],
);
}

Expand Down
27 changes: 27 additions & 0 deletions stringList.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,34 @@ t.test('withTrim().withReplaceAll("_")', (t) => {
.withReplaceAll(' ', '_');
testExpectedArrayValues(t, list, 'has_spaces', 'has_more_spaces');
testEscapingFromStringList(t, list, 'has_spaces', 'has_more_spaces');
t.end();
});

t.test('without()', (t) => {
const list = stringList('foo', 'bar').without('bar');
testExpectedArrayValues(t, list, 'foo');
testEscapingFromStringList(t, list, 'foo');

const list2 = stringList(
'foo',
'bar',
'bar2',
'foo2',
'bar3',
'foo3',
'bar4',
'foo4',
).without(
stringList('bar', 'foo'),
// @ts-expect-error (because of null/object in the parameters - added to cover the case)
'bar2',
stringList('bar3', 'foo3'),
'foo4',
null,
{ foo2: 'bar4' },
);
testExpectedArrayValues(t, list2, 'foo2', 'bar4');
testEscapingFromStringList(t, list2, 'foo2', 'bar4');
t.end();
});

Expand Down

0 comments on commit 3eba0ee

Please sign in to comment.