Skip to content

Commit

Permalink
create @automattic/js-utils package and add uniqueBy (Automattic#…
Browse files Browse the repository at this point in the history
…50263)

* lib: introduce `uniqueBy` helper

extract `uniqueBy` into `js-utils` pkg and use TS

remove comment about `null` and `undefined` values

* package.json: set `sideEffects` to `false`

* tsconfig.json: remove `DOM` and `DOM.Iterable` from lib

* update types

Co-authored-by: sarayourfriend <saram@fastmail.com>

* change version to 1.0.0

Co-authored-by: sarayourfriend <saram@fastmail.com>
  • Loading branch information
flootr and sarayourfriend authored Feb 23, 2021
1 parent 339e0ad commit 2b1c472
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/js-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 1.0.0

Add `uniqueBy`
3 changes: 3 additions & 0 deletions packages/js-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# JavaScript utils

This is a collection of general purpose JavaScript utility functions.
3 changes: 3 additions & 0 deletions packages/js-utils/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
preset: '../../test/packages/jest-preset.js',
};
37 changes: 37 additions & 0 deletions packages/js-utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@automattic/js-utils",
"version": "1.0.0",
"description": "General purpose Javascript utility functions",
"homepage": "https://github.com/Automattic/wp-calypso",
"license": "GPL-2.0-or-later",
"author": "Automattic Inc.",
"sideEffects": false,
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"calypso:src": "src/index.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/Automattic/wp-calypso.git",
"directory": "packages/js-utils"
},
"files": [
"dist",
"src"
],
"types": "dist/types",
"publishConfig": {
"access": "public"
},
"bugs": {
"url": "https://github.com/Automattic/wp-calypso/issues"
},
"scripts": {
"clean": "tsc --build ./tsconfig.json ./tsconfig-cjs.json --clean && npx rimraf dist",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"prepack": "yarn run clean && yarn run build",
"watch": "tsc --build ./tsconfig.json --watch"
},
"dependencies": {
"tslib": "^2.1.0"
}
}
1 change: 1 addition & 0 deletions packages/js-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as uniqueBy } from './unique-by';
55 changes: 55 additions & 0 deletions packages/js-utils/src/test/unique-by.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Internal dependencies
*/
import uniqueBy from '../unique-by';

describe( 'uniqueBy()', () => {
test( 'should remove duplicate values by identity by default (numbers)', () => {
const values = [ 1, 2, 3, 1, 2, 4 ];
expect( uniqueBy( values ) ).toEqual( [ 1, 2, 3, 4 ] );
} );

test( 'should remove duplicate values by identity by default (strings)', () => {
const values = [ 'a', 'b', 'c', 'd', 'a', 'b' ];
expect( uniqueBy( values ) ).toEqual( [ 'a', 'b', 'c', 'd' ] );
} );

test( 'should remove duplicate values by identity by default (mixed numbers & strings)', () => {
const values = [ 1, 2, 'a', 'b', 1, 2, 'c', 'a' ];
expect( uniqueBy( values ) ).toEqual( [ 1, 2, 'a', 'b', 'c' ] );
} );

test( 'should be able to handle `null` and `undefined` values', () => {
const values = [ null, null, undefined, undefined ];
expect( uniqueBy( values ) ).toEqual( [ null, undefined ] );
} );

test( 'should not remove anything if there are no duplicates', () => {
const values = [ 1, 2, 'a', 'b' ];
expect( uniqueBy( values ) ).toEqual( [ 1, 2, 'a', 'b' ] );
} );

test( 'should accept a custom compare function (String.trim)', () => {
const values = [ 'a', 'a ', ' a ', ' a', ' a' ];
const compare = ( a, b ) => a.trim() === b.trim();
expect( uniqueBy( values, compare ) ).toEqual( [ 'a' ] );
} );

test( 'should accept a custom compare function (parseInt)', () => {
const values = [ 1, '1', 2, 3, '3' ];
const compare = ( a, b ) => parseInt( a ) === parseInt( b );
expect( uniqueBy( values, compare ) ).toEqual( [ 1, 2, 3 ] );
} );

test( 'should accept a custom compare function (objects)', () => {
const values = [ { ID: 1 }, { ID: 1 }, { ID: 2 }, { ID: 3 } ];
const compare = ( a, b ) => a.ID === b.ID;
expect( uniqueBy( values, compare ) ).toEqual( [ { ID: 1 }, { ID: 2 }, { ID: 3 } ] );
} );

test( 'should return a new (shallow) array', () => {
const values = [ 1, '2', { ID: 1 } ];
expect( uniqueBy( values ) ).not.toBe( values );
expect( uniqueBy( values )[ 2 ] ).toBe( values[ 2 ] );
} );
} );
10 changes: 10 additions & 0 deletions packages/js-utils/src/unique-by.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const defaultCompare = ( a: any, b: any ): boolean => a === b;

function uniqueBy< T >( arr: T[], fn: ( a: T, b: T ) => boolean = defaultCompare ): T[] {
return arr.reduce( ( acc, v ) => {
if ( ! acc.some( ( x: any ) => fn( v, x ) ) ) acc.push( v );
return acc;
}, [] as T[] );
}

export default uniqueBy;
11 changes: 11 additions & 0 deletions packages/js-utils/tsconfig-cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig",
"compilerOptions": {
"module": "commonjs",
"declaration": false,
"declarationDir": null,
"outDir": "dist/cjs",
"composite": false,
"incremental": true
}
}
29 changes: 29 additions & 0 deletions packages/js-utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES5",
"lib": [ "ESNext" ],
"baseUrl": ".",
"module": "esnext",
"allowJs": false,
"jsx": "react",
"declaration": true,
"declarationDir": "dist/types",
"outDir": "dist/esm",
"rootDir": "src",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"typeRoots": [ "../../node_modules/@types" ],
"types": [ "node" ],
"noEmitHelpers": true,
"importHelpers": true,
"composite": true
},
"include": [ "src" ],
"exclude": [ "**/test/*" ]
}
1 change: 1 addition & 0 deletions packages/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
{ "path": "./data-stores" },
{ "path": "./domain-picker" },
{ "path": "./i18n-utils" },
{ "path": "./js-utils" },
{ "path": "./language-picker" },
{ "path": "./languages" },
{ "path": "./launch" },
Expand Down

0 comments on commit 2b1c472

Please sign in to comment.