Skip to content

Instantly share code, notes, and snippets.

@tohagan
Last active September 8, 2019 14:33
Show Gist options
  • Save tohagan/9d44d39dc4e9538aec4aa0c49404fae8 to your computer and use it in GitHub Desktop.
Save tohagan/9d44d39dc4e9538aec4aa0c49404fae8 to your computer and use it in GitHub Desktop.
Get / Set / Delete / Toggle / Increment Object Properties - VueJS Reactive Objects
import Vue from "vue";
function getProp(obj, props) {
let val = obj;
let i = 0;
for (i = 0; i < props.length - 1; i++) {
val = val[props[i]];
if (typeof val !== "object") return undefined;
}
return val[props[i]];
}
function setProp(obj, props, value) {
let val = obj;
let i = 0;
for (i = 0; i < props.length - 1; i++) {
if (typeof val[props[i]] !== "object") {
Vue.set(val, props[i], {});
}
val = val[props[i]];
}
Vue.set(val, props[i], value);
}
function deleteProp(obj, props) {
let val = obj;
let i = 0;
for (i = 0; i < props.length - 1; i++) {
val = val[props[i]];
if (typeof val !== "object") return;
}
Vue.delete(val, props[i]);
}
function toggleProp(obj, props, defaultValue = true) {
if (getProp(obj, props)) {
deleteProp(obj, props);
} else {
setProp(obj, props, defaultValue);
}
}
// Warning - Unsafe if you need this to be transational
function incrementProp(obj, props) {
const num = getProp(obj, props) || 0;
setProp(obj, props, num + 1);
}
export { getProp, setProp, deleteProp, toggleProp, incrementProp };
import "jest";
import { getProp, setProp, deleteProp, toggleProp, incrementProp } from "src/util/props";
describe("props.", () => {
let objJson = JSON.stringify({ a: { b: 1, d: { e: 4 } }, c: 2 });
let obj1, obj2;
beforeEach(() => {
obj1 = JSON.parse(objJson);
obj2 = JSON.parse(objJson);
});
describe("getProp", () => {
it("should return undefined when path is undefined", () => {
expect(getProp(obj1, ["b"])).toBeUndefined();
expect(getProp(obj1, ["a", "c"])).toBeUndefined();
});
it("should return value when path is defined", () => {
expect(getProp(obj1, ["c"])).toBe(2);
expect(getProp(obj1, ["a", "b"])).toBe(1);
});
it("should not alter object when all of path is undefined", () => {
expect(getProp(obj1, ["b"])).toBeUndefined();
expect(obj1).toEqual(obj2);
});
it("should not alter object when some of path is undefined", () => {
expect(getProp(obj1, ["a", "c"])).toBeUndefined();
expect(obj1).toEqual(obj2);
});
it("getProp should not alter path array", () => {
const path = ["a", "c"];
const pathCopy = [...path];
expect(getProp(obj1, path)).toBeUndefined();
expect(path).toEqual(pathCopy);
});
});
describe("setProp", () => {
it("should updated an existing property", () => {
setProp(obj1, ["a", "b"], 12);
obj2.a.b = 12;
expect(obj1).toEqual(obj2);
});
it("should add a missing property", () => {
setProp(obj1, ["b"], 12);
obj2.b = 12;
expect(obj1).toEqual(obj2);
});
it("should add missing partial path and property", () => {
setProp(obj1, ["a", "d", "f", "g", "h"], 12);
obj2.a.d.f = { g: { h: 12 } };
// console.log(JSON.stringify(obj1));
expect(obj1).toEqual(obj2);
});
it("setProp should not alter path array", () => {
const path = ["a", "c"];
const pathCopy = [...path];
expect(setProp(obj1, path, "xx")).toBeUndefined();
expect(path).toEqual(pathCopy);
});
});
describe("deleteProp", () => {
it("should remove an existing property", () => {
expect(getProp(obj1, ["a", "b"])).toBeDefined();
deleteProp(obj1, ["a", "b"]);
expect(getProp(obj1, ["a", "b"])).toBeUndefined();
delete obj2.a.b;
expect(obj1).toEqual(obj2);
});
it("should no change for a missing property", () => {
deleteProp(obj1, ["b"]);
expect(obj1).toEqual(obj2);
});
it("should no change deep missing", () => {
deleteProp(obj1, ["a", "d", "f", "g", "h"]);
expect(obj1).toEqual(obj2);
});
it("deleteProp should not alter path array", () => {
const path = ["a", "c"];
const pathCopy = [...path];
expect(deleteProp(obj1, path, "xx")).toBeUndefined();
expect(path).toEqual(pathCopy);
});
});
// { a: { b: 1, d: { e: 4 } }, c: 2 }
describe("toggleProp", () => {
it("toggle existing prop", () => {
expect(getProp(obj1, ["a", "b"])).toBeDefined();
toggleProp(obj1, ["a", "b"]);
delete obj2.a.b;
expect(obj1).toEqual(obj2);
expect(getProp(obj1, ["a", "b"])).toBeUndefined();
toggleProp(obj1, ["a", "b"]);
expect(getProp(obj1, ["a", "b"])).toBeDefined();
});
it("should toggle missing partial path and property", () => {
const path = ["a", "d", "f", "g", "h"];
expect(getProp(obj1, path)).toBeUndefined();
toggleProp(obj1, path);
obj2.a.d.f = { g: { h: true } };
expect(obj1).toEqual(obj2);
expect(getProp(obj1, path)).toBeDefined();
toggleProp(obj1, path);
expect(getProp(obj1, path)).toBeUndefined();
// won't completely reset back to obj1
obj2 = JSON.parse(objJson);
obj2.a.d.f = { g: {} };
expect(obj1).toEqual(obj2);
});
it("toggleProp should not alter path array", () => {
const path = ["a", "c"];
const pathCopy = [...path];
expect(toggleProp(obj1, path, "xx")).toBeUndefined();
expect(path).toEqual(pathCopy);
});
});
describe('incrementProp', () => {
it('should increment existing prop', () => {
expect(getProp(obj1, ["a", "b"])).toBe(1);
incrementProp(obj1, ["a", "b"]);
expect(getProp(obj1, ["a", "b"])).toBe(2);
incrementProp(obj1, ["a", "b"]);
expect(getProp(obj1, ["a", "b"])).toBe(3);
obj2.a.b = 3;
expect(obj1).toEqual(obj2);
});
});
});
@tohagan
Copy link
Author

tohagan commented Sep 8, 2019

Revised to ensure props path is isomorphic. Add Jest test suite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment