Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
TASK: Implement set operation for vanilla objects
Browse files Browse the repository at this point in the history
  • Loading branch information
grebaldi committed Feb 13, 2016
1 parent 0051862 commit f8d9999
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 2 deletions.
30 changes: 30 additions & 0 deletions src/migrations/atoms/set/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import createPolymorphFunction from '../../../util/createPolymorphFunction/index.js';
import resolveObjectPath from '../../../util/resolveObjectPath/index.js';

import $get from '../../../projections/atoms/get/index.js';

//
// Helper function to peform the necessary recursion
//
const recursivelySetValueInObject = (object, value, path) => {
if (path.length === 0) {
return value;
}

object[path[0]] = recursivelySetValueInObject(object[path[0]], value, path.slice(1));
return object;
};

//
// Sets a value inside an object and returns the resulting object
//
export default createPolymorphFunction(
path => value => subject => {
if (!$get(path, subject)) {
return subject;
}

const object = JSON.parse(JSON.stringify(subject));
return recursivelySetValueInObject(object, value, resolveObjectPath(path));
}
);
147 changes: 147 additions & 0 deletions src/migrations/atoms/set/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import {expect} from 'chai';

import $set from './index.js';

describe('Migrations > Atoms > $set', () => {
describe('Common', () => {
it('$set :: String -> * -> Object -> Object', () => {
expect($set('')).to.be.a('function');
expect($set('')(NaN)).to.be.a('function');
expect($set('')(NaN)({})).not.to.be.a('function');
});

it('$set :: (String, *) -> Object -> Object', () => {
expect($set('', NaN)).to.be.a('function');
expect($set('', NaN)({})).not.to.be.a('function');
});

it('$set :: (String, *, Object) -> Object', () => {
expect($set('', NaN, {})).not.to.be.a('function');
});
})

describe('Vanilla JS', () => {
it('should shallowly set a value inside an object', () => {
const subject = {
test: 'a'
};

expect($set('test', 'b', subject)).to.deep.equal({
test: 'b'
});
expect($set('test', 'b', subject)).to.not.equal(subject);
});

it('should shallowly set a value inside an array', () => {
const subject = [1, 2, 3];

expect($set(1, 42, subject)).to.deep.equal([1, 42, 3]);
expect($set(1, 42, subject)).to.not.equal(subject);
});

it('should deeply set a value inside an object', () => {
const subject = {
a: {
b: {
c: {
d: {
e: 'Former Value'
}
}
}
}
};

expect($set('a.b.c.d.e', 'New Value', subject)).to.deep.equal({
a: {
b: {
c: {
d: {
e: 'New Value'
}
}
}
}
});
expect($set('a.b.c.d.e', 'New Value', subject)).to.not.equal(subject);
});

it('should deeply set a value inside an array', () => {
const subject = [
[
[
[
['Former Value']
]
]
]
];

expect($set('0.0.0.0.0', 'New Value', subject)).to.deep.equal([
[
[
[
['New Value']
]
]
]
]);
expect($set('0.0.0.0.0', 'New Value', subject)).to.not.equal(subject);
});

it('should deeply set a value inside a mixed object/array structure', () => {
const subject = {
a: ['b', {
c: {
d: ['e', 'f']
}
}]
};

expect($set('a.0', 'test', subject)).to.deep.equal({
a: ['test', {
c: {
d: ['e', 'f']
}
}]
});
expect($set('a.0', 'test', subject)).to.not.equal(subject);
expect($set('a.1.c', 'test', subject)).to.deep.equal({
a: ['b', {
c: 'test'
}]
});
expect($set('a.1.c', 'test', subject)).to.not.equal(subject);
expect($set('a.1.c.d.1', 'test', subject)).to.deep.equal({
a: ['b', {
c: {
d: ['e', 'test']
}
}]
});
expect($set('a.1.c.d.1', 'test', subject)).to.not.equal(subject);
});
it('should do nothing if the path leads to undefined', () => {
const subject = {
a: ['b', {
c: {
d: ['e', 'f']
}
}]
};

expect($set('a.2', 'test', subject)).to.equal(subject);
expect($set('a.1.c.f', 'test', subject)).to.equal(subject);
expect($set('a.1.c.d.5', 'test', subject)).to.equal(subject);
});
});

describe('Immutable', () => {
it('should shallowly set a value inside an object');
it('should shallowly set a value inside an array');
it('should deeply set a value inside an object');
it('should deeply set a value inside an array');
it('should deeply set a value inside a mixed object/array structure');
it('should do nothing if the path leads to undefined');
});
});
4 changes: 2 additions & 2 deletions src/projections/atoms/get/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default createPolymorphFunction(
path => {
//
// This function returns the path, if it is neither
// an array nor a string
// an array nor a string nor a number
//
if (typeof path !== 'string' && !Array.isArray(path)) {
if (typeof path !== 'string' && typeof path !== 'number' && !Array.isArray(path)) {
return path;
}

Expand Down
4 changes: 4 additions & 0 deletions src/util/resolveObjectPath/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export default path => {
return path;
}

if (typeof path === 'number') {
return [path];
}

return path.split('.').map(part => {
const partAsInteger = parseInt(part);

Expand Down

0 comments on commit f8d9999

Please sign in to comment.