Skip to content

Commit

Permalink
Implements the deep equals functionalities
Browse files Browse the repository at this point in the history
  • Loading branch information
JesuHrz committed Feb 5, 2023
1 parent c041da8 commit 5896428
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 17 deletions.
104 changes: 104 additions & 0 deletions __tests__/deep-equals.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@

import { deepEquals } from '../src/deep-equals'

describe('DeepEquals', () => {
it('Should export deepEquals as named export', () => {
expect(deepEquals).toBeInstanceOf(Function)
})

it('Should compare strings', () => {
expect(deepEquals('string', 'string')).toBe(true)
expect(deepEquals('string', 'other-string')).toBe(false)
})

it('Should compare numbers', () => {
expect(deepEquals(1, 1)).toBe(true)
expect(deepEquals(1, 2)).toBe(false)
})

it('Should compare booleans', () => {
expect(deepEquals(true, true)).toBe(true)
expect(deepEquals(true, false)).toBe(false)
})

it('Should compare nulls', () => {
expect(deepEquals(null, null)).toBe(true)
expect(deepEquals(null, false)).toBe(false)
})

it('Should compare arrays', () => {
expect(deepEquals([1, 2, 3], [1, 2, 3])).toBe(true)
expect(deepEquals([1, 2, 3], [3, 2, 1])).toBe(false)
})

it('Should compare arrays of objects', () => {
expect(deepEquals(
[{ a: 1, b: 2, c: 'c' }, { a: 1, b: 2, c: 'c' }],
[{ a: 1, b: 2, c: 'c' }, { a: 1, b: 2, c: 'c' }]
)).toBe(true)
expect(deepEquals(
[{ a: 1, b: 2, c: 'c' }, { a: 1, b: 2, c: 'c' }],
[{ a: 2, b: 1, c: 'c' }, { a: 1, b: 2, c: 'c' }]
)).toBe(false)
})

it('Should compare simple objects', () => {
expect(deepEquals({ a: 1, b: 2, c: 'c' }, { a: 1, b: 2, c: 'c' })).toBe(true)
expect(deepEquals({ a: 1, b: 2, c: 'c' }, { a: 2, b: 1 })).toBe(false)
})

it('Should compare nested objects', () => {
expect(deepEquals(
{ a: 1, b: 2, c: { y: 2, z: '1' } },
{ a: 1, b: 2, c: { y: 2, z: '1' } }
)).toBe(true)
expect(deepEquals(
{ a: 1, b: 2, c: { y: 2, z: '1' } },
{ a: 1, b: 2, c: { y: 2, z: '2' } }
)).toBe(false)
})

it('Should compare functions', () => {
const foo = () => 'foo'
const bar = () => 'bar'

expect(deepEquals(foo, foo)).toBe(true)
expect(deepEquals(foo, bar)).toBe(false)
})

it('Should compare Maps', () => {
const firstMap = new Map([['Map', { name: 'I am a map', phone: '213-555-1234' }]])
const secondMap = new Map([['Map', { name: 'I am a map', phone: '213-555-1234' }]])
const thirdMap = new Map([
['Map', { name: 'I am third map', phone: '213-555-1234' }],
[1, 1]
])

expect(deepEquals(firstMap, firstMap)).toBe(true)
expect(deepEquals(firstMap, secondMap)).toBe(true)
expect(deepEquals(firstMap, thirdMap)).toBe(false)
})

it('Should compare Sets', () => {
const firstSet = new Set(['1', { name: 'I am a set', phone: '213-555-1234' }])
const secondSet = new Set(['1', { name: 'I am a set', phone: '213-555-1234' }])
const thirdSet = new Set(
['2', { name: 'I am another', phone: '213-555-1234' }, 2]
)

expect(deepEquals(firstSet, secondSet)).toBe(true)
expect(deepEquals(firstSet, thirdSet)).toBe(false)
})

it('Should compare Dates', () => {
expect(deepEquals(
new Date('2023-01-04T00:00:00.000Z'),
new Date('2023-01-04T00:00:00.000Z')
)).toBe(true)

expect(deepEquals(
new Date('2023-01-04T00:00:00.000Z'),
new Date('2023-01-05T00:00:00.000Z')
)).toBe(false)
})
})
29 changes: 18 additions & 11 deletions examples/vanilla/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,48 @@
<title>Store - Vanilla</title>
</head>
<body>
<p>Count: <span class="js-count">0</span></p>
<p>Counter: <span class="js-counter">0</span></p>
<button class="js-increment">+1</button>
<button class="js-reset">Restore to inital value</button>
<button class="js-reset-from-scratch">Restore from scratch</button>
<script src="../../dist/killa.min.js"></script>
<script>
const killa = window.killa

const $text = document.querySelector('.js-count')
const $text = document.querySelector('.js-counter')
const $button = document.querySelector('.js-increment')
const $restoreOne = document.querySelector('.js-reset')
const $restoreTwo = document.querySelector('.js-reset-from-scratch')

const store = killa.createStore({ count: 0, text: '' })
const store = killa.createStore({ counter: 0, text: '', filter: '' })

window.addEventListener('DOMContentLoaded', () => {
store.subscribe((state, prevState) => {
console.log('Updated state', store.getState())
console.log('Global subscriber', store.getState())
})

store.subscribe((state) => {
console.log('Updated count state', state)
$text.textContent = store.getState().count
}, (state) => state.count)
console.log('Counter state subscriber', state.counter)
$text.textContent = state.counter
}, (state) => state.counter)

store.subscribe(() => {
console.log('Updated text state', store.getState())
store.subscribe((state) => {
console.log('Text state subscriber', state.counter)
}, (state) => state.text)

store.subscribe((state) => {
console.log('Counter and filter state subscriber', state.counter)
}, (state) => ({ counter: state.counter, filter: state.filter }))

store.subscribe((state) => {
console.log('Counter and filter state subscriber', state.counter)
}, (state) => [{ counter: state.counter, filter: state.filter }])

$button.addEventListener('click', () => {
store.setState((state) => {
return {
...state,
count: state.count + 1
counter: state.counter + 1
}
})
})
Expand All @@ -56,7 +64,6 @@
}, true)
})
})

</script>
</body>
</html>
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SYMBOL_LISTENER = Symbol('@@killa-listener')
15 changes: 9 additions & 6 deletions src/core.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import clone from 'clone'

import { SYMBOL_LISTENER } from './constants'
import { deepEquals } from './deep-equals'

export const createStore = (initialState = {}, options = {}) => {
const listeners = new Set()
let state = clone(initialState)
Expand All @@ -25,11 +28,11 @@ export const createStore = (initialState = {}, options = {}) => {
const _newState = getState()

if (listener.LISTENER) {
const currentState = listener.STATE
const nextState = listener.SELECTOR(state)
const selectorState = listener.SELECTOR_STATE
const nextselectorState = listener.SELECTOR(state)

if (!Object.is(currentState, nextState)) {
listener.STATE = nextState
if (!deepEquals(selectorState, nextselectorState)) {
listener.SELECTOR_STATE = nextselectorState
listener(_newState, _prevState)
}

Expand All @@ -43,8 +46,8 @@ export const createStore = (initialState = {}, options = {}) => {

const subscribe = (listener, selector) => {
if (selector) {
listener.LISTENER = Symbol('@@killa-listener')
listener.STATE = selector(state)
listener.LISTENER = SYMBOL_LISTENER
listener.SELECTOR_STATE = selector(state)
listener.SELECTOR = selector
}

Expand Down
67 changes: 67 additions & 0 deletions src/deep-equals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const isObject = (object) => {
return object !== null && typeof object === 'object'
}

export const deepEquals = (typeA, typeB) => {
if (!isObject(typeA) && !isObject(typeB) && Object.is(typeA, typeB)) {
return true
}

if (typeA instanceof Date && typeB instanceof Date) {
return Object.is(typeA.getTime(), typeB.getTime())
}

if (
!isObject(typeA) || typeA === null ||
!isObject(typeB) || typeB === null
) {
return false
}

if (typeA instanceof Map && typeB instanceof Map) {
if (typeA.size !== typeB.size) {
return false
}

for (const item of typeA) {
const key = item[0]
const value = item[1]

if (!Object.is(value, typeB.get(key))) {
return deepEquals(value, typeB.get(key))
}
}

return true
}

if (typeA instanceof Set && typeB instanceof Set) {
if (typeA.size !== typeB.size) {
return false
}

return deepEquals([...typeA.values()], [...typeB.values()])
}

const keysA = Object.keys(typeA)
const keysB = Object.keys(typeB)

if (keysA.length !== keysB.length) {
return false
}

for (const key of keysA) {
const valueA = typeA[key]
const valueB = typeB[key]
const areObjects = isObject(valueA) && isObject(valueB)

if (
(areObjects && !deepEquals(valueA, valueB)) ||
(!areObjects && !Object.is(valueA, valueB))
) {
return false
}
}

return true
}

0 comments on commit 5896428

Please sign in to comment.