import cloneDeep from 'lodash/cloneDeep'; import flattenDeep from 'lodash/flattenDeep'; import compact from 'lodash/compact'; import { JSONPath } from 'jsonpath-plus'; import Vue from 'vue'; import transform from 'lodash/transform'; import isObject from 'lodash/isObject'; import isEqual from 'lodash/isEqual'; import difference from 'lodash/difference'; import { splitObjectPath } from '@/utils/string'; export function set(obj, path, value) { let ptr = obj; if (!ptr) { return; } const parts = splitObjectPath(path); for (let i = 0; i < parts.length; i++) { const key = parts[i]; if ( i === parts.length - 1 ) { Vue.set(ptr, key, value); } else if ( !ptr[key] ) { // Make sure parent keys exist Vue.set(ptr, key, {}); } ptr = ptr[key]; } return obj; } export function get(obj, path) { if ( path.startsWith('$') ) { try { return JSONPath({ path, json: obj, wrap: false, }); } catch (e) { console.log('JSON Path error', e, path, obj); // eslint-disable-line no-console return '(JSON Path err)'; } } if ( !path.includes('.') ) { return obj?.[path]; } const parts = splitObjectPath(path); for (let i = 0; i < parts.length; i++) { if (!obj) { return; } obj = obj[parts[i]]; } return obj; } export function getter(path) { return function(obj) { return get(obj, path); }; } export function clone(obj) { return cloneDeep(obj); } export function isEmpty(obj) { if ( !obj ) { return true; } return !Object.keys(obj).length; } /** * Checks to see if the object is a simple key value pair where all values are * just primitives. * @param {any} obj */ export function isSimpleKeyValue(obj) { return obj !== null && !Array.isArray(obj) && typeof obj === 'object' && Object.values(obj || {}).every(v => typeof v !== 'object'); } /* returns an object with no key/value pairs (including nested) where the value is: empty array empty object null undefined */ export function cleanUp(obj) { Object.keys(obj).map((key) => { const val = obj[key]; if ( Array.isArray(val) ) { obj[key] = compact(val.map((each) => { if (each) { const cleaned = cleanUp(each); if (!isEmpty(cleaned)) { return cleaned; } } })); if (compact(obj[key]).length === 0) { delete obj[key]; } } else if (typeof val === 'undefined' || val === null) { delete obj[key]; } else if ( isObject(val) ) { if (isEmpty(val)) { delete obj[key]; } obj[key] = cleanUp(val); } }); return obj; } export function definedKeys(obj) { const keys = Object.keys(obj).map((key) => { const val = obj[key]; if ( Array.isArray(val) ) { return key; } else if ( isObject(val) ) { return ( definedKeys(val) || [] ).map(subkey => `${ key }.${ subkey }`); } else { return key; } }); return compact(flattenDeep(keys)); } export function diff(from, to) { from = from || {}; to = to || {}; // Copy values in 'to' that are different than from const out = transform(to, (res, toVal, k) => { const fromVal = from[k]; if ( isEqual(toVal, fromVal) ) { return; } if ( Array.isArray(toVal) || Array.isArray(fromVal) ) { // Don't diff arrays, just use the whole value res[k] = toVal; } else if ( isObject(toVal) && isObject(from[k]) ) { res[k] = diff(fromVal, toVal); } else { res[k] = toVal; } }); const fromKeys = definedKeys(from); const toKeys = definedKeys(to); const missing = difference(fromKeys, toKeys); for ( const k of missing ) { set(out, k, null); } return out; } export function nonEmptyValueKeys(obj) { const validKeys = Object.keys(obj).map((key) => { const val = obj[key]; if ( isObject(val) ) { const recursed = nonEmptyValueKeys(val); if (recursed) { return recursed.map((subkey) => { return `"${ key }"."${ subkey }"`; }); } } else if ( Array.isArray(val) ) { if (compact(val).length) { return key; } } else if (!!val || val === false || val === 0) { return key; } }); return compact(flattenDeep(validKeys)); }