Skip to content

Commit

Permalink
Refactor (#2)
Browse files Browse the repository at this point in the history
This is quite a large refactoring of the codebase, which has the following changes:

- `reshaper` now exports a function, and can be used directly. `findShape` is therefore deprecated.
- Error handling should be much more reliable. Exceptions are now used throughout, instead of returning `null`.
- Files and functions are generally more separated out
- No more global stacks - these are passed around
- Backoff is improved with use of a `staleKeys` set
- Key-fixing is improved with use of a `goodKeys` set, rather than the slightly fragile hint array rotation.
  • Loading branch information
JoelOtter committed May 30, 2016
1 parent b5731ca commit c58fd26
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 234 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ To see some interactive examples, check out this [Kajero notebook](http://www.jo

## Usage

#### `reshaper.findShapes(data, schema, [hint])`
#### `reshaper(data, schema, [hint])`

- `data`: The JavaScript data structure to be reshaped.
- `schema`: The structure we want our reshaped data to match.
Expand Down Expand Up @@ -46,11 +46,11 @@ var peopleData = [

var schema = ['String'];

reshaper.findShape(peopleData, schema);
reshaper(peopleData, schema);
// => ['Joel', 'Jake']

// We can give a 'hint', to say lastName is what we want.
reshaper.findShape(peopleData, schema, 'lastName');
reshaper(peopleData, schema, 'lastName');
// => ['Auterson', 'Hall']

// Object keys get used as hints
Expand All @@ -59,7 +59,7 @@ var schema = {
height: ['Number']
};

reshaper.findShape(peopleData, schema);
reshaper(peopleData, schema);
/* =>
{
age: [22, 24],
Expand Down
167 changes: 167 additions & 0 deletions lib/extract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Functions for extracting - creating the structure defined in the schema
*/

var util = require('./util');
var traverseAndFind = require('./traverse');

function getExtractionFunction (schema) {
var type = util.typeString(schema, true);
switch (type) {
case 'Array':
return extractArray;
case 'Number':
return extractNumber;
case 'String':
return extractString;
case 'Object':
return extractObject;
case 'Boolean':
return extractBoolean;
}
}

function extractObject(data, schema, stacks, hints) {
stacks.data.unshift(data);
var newStacks = {
data: stacks.data,
staleKeys: stacks.staleKeys.slice(),
goodKeys: stacks.goodKeys.slice()
};
var found;
try {
found = traverseAndFind(data, 'Object', newStacks, hints);
} catch (err) {
// If not found, create some dummy data. This will allow the
// backtracking to work, rather than giving up here.
found = {value: undefined};
}
var object = found.value;
var result = {value: {}};
var keys = Object.getOwnPropertyNames(schema);
keys.forEach(function (key) {
var valueType = schema[key];
var extractionFunction = getExtractionFunction(valueType);
var hintType = util.typeString(hints);
var newHints;
if (hintType === 'Object') {
if (hints.hasOwnProperty(key)) {
var child = hints[key];
if (util.typeString(child) === 'Array') {
newHints = child.slice();
newHints.push(key);
} else if (util.typeString(child) === 'Object') {
newHints = child;
} else {
newHints = [child];
newHints.push(key);
}
} else {
newHints = hints;
}
} else {
newHints = hints.slice();
newHints.push(key);
}
var extracted = extractionFunction(object, valueType, newStacks, newHints);
result.value[key] = extracted.value;
if (extracted.match) {
newStacks.staleKeys.push(extracted.match);
}
});
return result;
}

function extractArrayFromBacktrack(data, schema, stacks, hints) {
if (stacks.data.length > 0) {
return traverseAndFind(stacks.data[0], 'Array', stacks, hints);
}
throw new util.NotFoundException(data, schema, hints);
}

function extractArray(data, schema, stacks, hints) {
var arrayOf = schema[0];
var newStacks = {
data: stacks.data,
staleKeys: stacks.staleKeys.slice(),
goodKeys: stacks.goodKeys.slice()
};
var array;
try {
array = traverseAndFind(data, 'Array', newStacks, hints);
} catch (err) {
// Can't find? Might have gone too deep - use stack.
try {
array = extractArrayFromBacktrack(data, schema, newStacks, hints);
} catch (err) {
// TODO should probably prefer to convert to array of key/[vals] objects
// Still nothing? OK. Let's make an array out of object keys.
array = objectToArray(data, arrayOf);
}
}
var result = {value: []};
if (array.match !== undefined) {
result.match = array.match;
}
// We've got the array from data, let's construct our new one
var childExtractor = getExtractionFunction(arrayOf);
var foundMatch;
array.value.forEach(function (item) {
try {
var found = childExtractor(item, arrayOf, newStacks, hints);
result.value.push(found.value);
if (found.match !== undefined) {
foundMatch = found.match;
if (newStacks.goodKeys.indexOf(found.match) === -1) {
newStacks.goodKeys.unshift(found.match);
}
}
} catch (err) {
// Don't add to result array
}
});
result.match = foundMatch;
if (result.value.length === 0) {
if (array.match) {
newStacks.staleKeys.push(array.match);
result = extractArray(data, schema, newStacks);
} else {
throw new util.NotFoundException(data, schema, hints);
}
}
return result;
}

function objectToArray(object, type) {
if (util.typeString(object) !== 'Object') {
throw new util.NotFoundException(object, type);
}
var array = [];
var keys = Object.getOwnPropertyNames(object);
for (var i = 0; i < keys.length; i++) {
var data = object[keys[i]];
if (util.typeString(data) === type) {
array.push(data);
}
}
// We don't want to return an empty array.
if (array.length === 0) {
throw new util.NotFoundException(object, type);
}
return {value: array};
}

function extractNumber(data, schema, stacks, hints) {
return traverseAndFind(data, 'Number', stacks, hints);
}

function extractString(data, schema, stacks, hints) {
return traverseAndFind(data, 'String', stacks, hints);
}

function extractBoolean(data, schema, stacks, hints) {
return traverseAndFind(data, 'Boolean', stacks, hints);
}


module.exports = getExtractionFunction;
Loading

0 comments on commit c58fd26

Please sign in to comment.