dashboard/utils/object.js

212 lines
4.4 KiB
JavaScript

import cloneDeep from 'lodash/cloneDeep';
import flattenDeep from 'lodash/flattenDeep';
import compact from 'lodash/compact';
import jsonpath from 'jsonpath';
import Vue from 'vue';
import transform from 'lodash/transform';
import isObject from 'lodash/isObject';
import isEqual from 'lodash/isEqual';
import difference from 'lodash/difference';
const quotedKey = /['"]/;
export function set(obj, path, value) {
let ptr = obj;
let parts;
if (!ptr) {
return;
}
if ( path.match(quotedKey) ) {
// Path with quoted section
parts = path.match(/[^."']+|"([^"]*)"|'([^']*)'/g).map(x => x.replace(/['"]/g, ''));
} else {
// Regular path
parts = path.split('.');
}
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.query(obj, path)[0];
} catch (e) {
console.log('JSON Path error', e); // eslint-disable-line no-console
return '(JSON Path err)';
}
}
let parts;
if ( path.match(quotedKey) ) {
// Path with quoted section
parts = path.match(/[^."']+|"([^"]*)"|'([^']*)'/g).map(x => x.replace(/['"]/g, ''));
} else {
// Regular path
parts = path.split('.');
}
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) {
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));
}