Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Rework intersect, add overlap #8

Merged
merged 7 commits into from
Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions .commitlintrc
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"subject-case": [
2,
"never",
[
"lower-case",
"upper-case",
"camel-case",
"kebab-case",
"pascal-case",
"snake-case",
"start-case"
]
]
}
"extends": ["@commitlint/config-conventional"]
}
3 changes: 2 additions & 1 deletion .releaserc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
"changelogFile": "CHANGELOG.md",
"changelogTitle": "# A log of changes"
}
],
[
Expand Down
5,111 changes: 2,956 additions & 2,155 deletions package-lock.json

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,36 +52,36 @@
"publish": "semantic-release"
},
"dependencies": {
"@babel/runtime-corejs3": "^7.13.10",
"core-js": "^3.9.1",
"@babel/runtime-corejs3": "^7.13.17",
"core-js": "^3.11.0",
"fast-deep-equal": "^3.1.3",
"rfc-3986": "^1.0.1"
},
"devDependencies": {
"@asd14/eslint-config": "^5.31.0",
"@babel/cli": "^7.13.10",
"@babel/core": "^7.13.13",
"@asd14/eslint-config": "^5.32.1",
"@babel/cli": "^7.13.16",
"@babel/core": "^7.13.16",
"@babel/eslint-parser": "^7.13.10",
"@babel/plugin-transform-runtime": "^7.13.10",
"@babel/preset-env": "^7.13.12",
"@babel/register": "^7.13.8",
"@babel/register": "^7.13.16",
"@commitlint/cli": "^12.1.1",
"@commitlint/config-conventional": "^12.1.1",
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/git": "^9.0.0",
"benchmark": "^2.1.4",
"chi-squared-test": "^1.1.0",
"coveralls": "^3.1.0",
"documentation": "^13.2.0",
"eslint": "^7.23.0",
"eslint-config-prettier": "^8.1.0",
"documentation": "^13.2.5",
"eslint": "^7.25.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsdoc": "^32.3.0",
"eslint-plugin-jsdoc": "^32.3.2",
"eslint-plugin-json": "^2.1.2",
"eslint-plugin-no-inferred-method-name": "^2.0.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-unicorn": "^29.0.0",
"eslint-plugin-unicorn": "^31.0.0",
"lint-staged": "^10.5.4",
"lodash": "^4.17.21",
"markdownlint-cli": "^0.27.1",
Expand All @@ -95,6 +95,6 @@
"semantic-release": "^17.4.2",
"tap-nirvana": "^1.1.0",
"tape": "^5.2.2",
"underscore": "^1.12.1"
"underscore": "^1.13.1"
}
}
6 changes: 3 additions & 3 deletions src/distinct/distinct.bench.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable no-sync, no-console */

const Benchmark = require("benchmark")
import Benchmark from "benchmark"

const distinct = require("./distinct")
const { uniq: distinctR } = require("ramda")
import { distinct } from "./distinct"
import { uniq as distinctR } from "ramda"

const sourceA = [1, 2, 3, 3, 4]
const sourceB = [1, 2, [{ a: 2 }], [{ a: 2 }]]
Expand Down
31 changes: 18 additions & 13 deletions src/distinct/distinct.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { any } from "../any/any"
import { isDeepEqual } from "../deep-equal/deep-equal"
import { curry } from "../curry/curry"
import { isEqual } from "../is-equal/is-equal"

const _distinctBy = (predicateFn, source) => {
const result = []

for (let i = 0, length = source.length; i < length; i++) {
const newElement = source[i]

if (any(item => predicateFn(newElement, item), result) === false) {
result.push(newElement)
}
}

return result
}

/**
* Remove repeating values
Expand All @@ -16,16 +31,6 @@ import { isDeepEqual } from "../deep-equal/deep-equal"
* distinct([1, 1, 2])
* // => [1, 2]
*/
export const distinct = source => {
const result = []

for (let i = 0, length = source.length; i < length; i++) {
const newElement = source[i]
export const distinct = source => _distinctBy(isEqual, source)

if (any(isDeepEqual(newElement), result) === false) {
result.push(newElement)
}
}

return result
}
export const distinctBy = curry(_distinctBy)
51 changes: 30 additions & 21 deletions src/distinct/distinct.test.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import test from "tape"
import deepEquals from "fast-deep-equal"
import { describe } from "riteway"

import { distinct } from "./distinct"
import { distinct, distinctBy } from "./distinct"

test("distinct", t => {
t.deepEqual(
distinct([1, 1, 3]),
[1, 3],
"Primitives: ([1, 1 ,3]) // => [1, 3]"
)
describe("distinct", async assert => {
assert({
given: "empty array",
should: "return empty array",
actual: distinct([]),
expected: [],
})

t.deepEqual(
distinct([1, "1", 3]),
[1, "1", 3],
'Primitives: ([ 1, "1" ,3 ]) // => [ 1, "1" ,3 ]'
)
assert({
given: "array with duplicate items of same type",
should: "return array with unique items",
actual: distinct([1, 1, 3]),
expected: [1, 3],
})

t.deepEqual(
distinct([1, { a: 2 }, { a: 2 }]),
[1, { a: 2 }],
"Recursive: ([1, {a: 2}, {a: 2}]) // => [1, {a: 2}]"
)

t.deepEqual(distinct([]), [], "Empty array should return empty array")
assert({
given: "array with unique items",
should: "return array with same items",
actual: distinct([1, "1", 3]),
expected: [1, "1", 3],
})
})

t.end()
describe("distinctBy", async assert => {
assert({
given: "empty array",
should: "return empty array",
actual: distinctBy(deepEquals, [1, { a: 2 }, { a: 2 }]),
expected: [1, { a: 2 }],
})
})
9 changes: 5 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const fastDeepEqual = require("fast-deep-equal")
import fastDeepEqual from "fast-deep-equal"

export { fastDeepEqual as isDeepEqual, fastDeepEqual as deepEqual }
export { elapsedTime } from "./elapsed-time/elapsed-time"
Expand Down Expand Up @@ -67,7 +67,7 @@ export { max, maxBy } from "./max/max"
export { min, minBy } from "./min/min"
export { move } from "./move/move"
export { flatten } from "./flatten/flatten"
export { distinct } from "./distinct/distinct"
export { distinct, distinctBy } from "./distinct/distinct"
export { dropLast } from "./drop-last/drop-last"
export { upsertWith as upsert, upsertWith } from "./upsert/upsert"
export { append, append as concat } from "./append/append"
Expand All @@ -88,7 +88,8 @@ export { hasKey } from "./has-key/has-key"
export { filter, filterWith } from "./filter/filter"
export { find, findWith } from "./find/find"
export { findIndex, findIndexWith } from "./find-index/find-index"
export { intersect } from "./intersect/intersect"
export { join, joinBy } from "./join/join"
export { intersect, intersectBy } from "./intersect/intersect"

// Number
export { dec } from "./dec/dec"
Expand All @@ -106,6 +107,6 @@ export { toLower } from "./to-lower/to-lower"
export { toUpper } from "./to-upper/to-upper"
export { trim } from "./trim/trim"
export { contains } from "./contains/contains"
export { join } from "./join/join"
export { unite } from "./unite/unite"
export { escapeHTML } from "./escape-html/escape-html"
export { escapeRegExp } from "./escape-reg-exp/escape-reg-exp"
4 changes: 3 additions & 1 deletion src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const test = require("tape")
/* eslint-disable unicorn/prefer-module */

import test from "tape"

test("index", t => {
t.doesNotThrow(() => {
Expand Down
96 changes: 82 additions & 14 deletions src/intersect/intersect.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,96 @@
import { findIndex } from "../find-index/find-index"
import { find } from "../find/find"
import { curry } from "../curry/curry"
import { any } from "../any/any"
import { isEqual } from "../is-equal/is-equal"

const _intersect = (predicateFn, unionFn, aList, bList) => {
if (aList.length === 0) {
return bList
const _intersectBy = (predicateFn, mergeFn, aList, bList) => {
if (aList.length === 0 || bList.length === 0) {
return []
}

if (bList.length === 0) {
return aList
}

const result = [...aList]
const result = []

for (const b of bList) {
const aIndex = findIndex(item => predicateFn(item, b))(result)
const a = find(item => predicateFn(item, b), undefined, aList)

if (a) {
const alreadyAdded = any(item => predicateFn(item, a), result)

if (aIndex === -1) {
result.push(b)
} else {
result[aIndex] = unionFn(result[aIndex], b)
if (!alreadyAdded) {
result.push(mergeFn(a, b))
}
}
}

return result
}

const _intersect = (aList, bList) => _intersectBy(isEqual, a => a, aList, bList)

/**
* Combine two arrays into one a set (array of unique items), composed of common
* items from both arrays.
*
* The function starts with an empty array and each item of bList will be
* searched in aList using a shallow equal. If it's found, it's also checked
* if it has not been already added.
*
* @param {any[]} aList
* @param {any[]} bList
*
* @returns {any[]}
*
* @name intersect
* @tag Array
* @signature (aList: any[], bList: any[]): any[]
*
* @see {@link intersectBy}
* @see {@link overlap}
* @see {@link overlapBy}
*
* @example
* intersect([1, 2, 3, 3], [3, 3, 4, 5])
* // => [3]
*/
export const intersect = curry(_intersect)

/**
* Combine two arrays into one a set (array of unique items), composed of common
* items from both arrays. Allow custom predicate and merge functions.
*
* The function starts with an empty array and each item of bList will be
* searched in aList using a shallow equal. If it's found, it's also checked
* if it has not been already added.
*
* @param {Function} predicateFn
* @param {Function} mergeFn
* @param {any[]} aList
* @param {any[]} bList
*
* @returns {any[]}
*
* @name intersectBy
* @tag Array
* @signature (predicateFn: Funciton, mergeFn: Function, aList: any[], bList: any[]): any[]
*
* @see {@link intersect}
* @see {@link overlap}
* @see {@link overlapBy}
*
* @example
* intersectBy(
* (a, b) => a.id === b.id,
* (a, b) => ({ ...a, ...b }),
* [
* { id: 1, lorem: "ipsum" },
* { id: 2, foo: "bar1" },
* { id: 2, foo: "bar2" },
* ],
* [
* { id: 2, comments: [] },
* { id: 3, comments: [] },
* ]
* )
* // => [{ id: 2, foo: "bar1", comments: [] }]
*/
export const intersectBy = curry(_intersectBy)
Loading