Skip to content

Commit

Permalink
Copy fastMerge util from react-native-onyx
Browse files Browse the repository at this point in the history
  • Loading branch information
blazejkustra committed Jan 23, 2024
1 parent 0a89938 commit 3586d6d
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
14 changes: 14 additions & 0 deletions lib/fastMerge.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true
*
* We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
* To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
*/
declare function fastMerge<T>(
target: T,
source: T,
shouldRemoveNullObjectValues: boolean
): T;

export default fastMerge;
103 changes: 103 additions & 0 deletions lib/fastMerge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import _ from "underscore";

Check failure on line 1 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote

// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1

/**
* @param {mixed} val
* @returns {boolean}
*/
function isMergeableObject(val) {
const nonNullObject = val != null ? typeof val === "object" : false;

Check failure on line 10 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote
return (
nonNullObject &&

Check failure on line 12 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

'&&' should be placed at the beginning of the line
Object.prototype.toString.call(val) !== "[object RegExp]" &&

Check failure on line 13 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote

Check failure on line 13 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

'&&' should be placed at the beginning of the line
Object.prototype.toString.call(val) !== "[object Date]" &&

Check failure on line 14 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

Strings must use singlequote

Check failure on line 14 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

'&&' should be placed at the beginning of the line
// eslint-disable-next-line rulesdir/prefer-underscore-method

Check failure on line 15 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'rulesdir/prefer-underscore-method' was not found
!Array.isArray(val)
);
}

/**
* @param {Object} target
* @param {Object} source
* @param {Boolean} shouldRemoveNullObjectValues
* @returns {Object}
*/
function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
const destination = {};
if (isMergeableObject(target)) {
// lodash adds a small overhead so we don't use it here
// eslint-disable-next-line rulesdir/prefer-underscore-method

Check failure on line 30 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'rulesdir/prefer-underscore-method' was not found
const targetKeys = Object.keys(target);
for (let i = 0; i < targetKeys.length; ++i) {
const key = targetKeys[i];

// If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object
const isSourceOrTargetNull =

Check failure on line 36 in lib/fastMerge.js

View workflow job for this annotation

GitHub Actions / lint

There should be no line break before or after '='
target[key] === null || source[key] === null;
const shouldOmitSourceKey =
shouldRemoveNullObjectValues && isSourceOrTargetNull;

if (!shouldOmitSourceKey) {
destination[key] = target[key];
}
}
}

// lodash adds a small overhead so we don't use it here
// eslint-disable-next-line rulesdir/prefer-underscore-method
const sourceKeys = Object.keys(source);
for (let i = 0; i < sourceKeys.length; ++i) {
const key = sourceKeys[i];

// If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object
const shouldOmitSourceKey =
shouldRemoveNullObjectValues && source[key] === null;

// If we pass undefined as the updated value for a key, we want to generally ignore it
const isSourceKeyUndefined = source[key] === undefined;

if (!isSourceKeyUndefined && !shouldOmitSourceKey) {
const isSourceKeyMergable = isMergeableObject(source[key]);

if (isSourceKeyMergable && target[key]) {
if (!shouldRemoveNullObjectValues || isSourceKeyMergable) {
// eslint-disable-next-line no-use-before-define
destination[key] = fastMerge(
target[key],
source[key],
shouldRemoveNullObjectValues
);
}
} else if (!shouldRemoveNullObjectValues || source[key] !== null) {
destination[key] = source[key];
}
}
}

return destination;
}

/**
* Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true
*
* We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
* To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
*
* @param {Object|Array} target
* @param {Object|Array} source
* @param {Boolean} shouldRemoveNullObjectValues
* @returns {Object|Array}
*/
function fastMerge(target, source, shouldRemoveNullObjectValues = true) {
// We have to ignore arrays and nullish values here,
// otherwise "mergeObject" will throw an error,
// because it expects an object as "source"
if (_.isArray(source) || source === null || source === undefined) {
return source;
}
return mergeObject(target, source, shouldRemoveNullObjectValues);
}

export default fastMerge;

0 comments on commit 3586d6d

Please sign in to comment.