Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 72 additions & 12 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ JSONPath.prototype.parent = function(obj, string) {
assert.ok(string, "we need a path");

var node = this.nodes(obj, string)[0];
var key = node.path.pop(); /* jshint unused:false */
return this.value(obj, node.path);
return _get_value_by_path(obj, node.path, 1);
}

JSONPath.prototype.apply = function(obj, string, fn) {
Expand All @@ -33,19 +32,37 @@ JSONPath.prototype.apply = function(obj, string, fn) {
assert.ok(string, "we need a path");
assert.equal(typeof fn, "function", "fn needs to be function")

var nodes = this.nodes(obj, string).sort(function(a, b) {
return this._iterateNodes(obj, string, function(node) {
var key = node.path[node.path.length - 1];
var parent = _get_value_by_path(obj, node.path, 1);
var val = node.value = fn.call(obj, parent[key]);
parent[key] = val;
});
}

JSONPath.prototype.forEachNode = function(obj, path, fn) {

assert.ok(obj instanceof Object, "obj needs to be an object");
assert.ok(path, "we need a path");
assert.equal(typeof fn, "function", "fn needs to be function")

return this._iterateNodes(obj, path, function(node) {
fn(new _NodeWrapper(obj, node.path, node.value));
});
}

JSONPath.prototype._iterateNodes = function(obj, path, fn) {

assert.ok(obj instanceof Object, "obj needs to be an object");
assert.ok(path, "we need a path");
assert.equal(typeof fn, "function", "fn needs to be function")

var nodes = this.nodes(obj, path).sort(function(a, b) {
// sort nodes so we apply from the bottom up
return b.path.length - a.path.length;
});

nodes.forEach(function(node) {
var key = node.path.pop();
var parent = this.value(obj, this.stringify(node.path));
var val = node.value = fn.call(obj, parent[key]);
parent[key] = val;
}, this);

return nodes;
return nodes.forEach(fn.bind(this));
}

JSONPath.prototype.value = function(obj, path, value) {
Expand All @@ -57,7 +74,7 @@ JSONPath.prototype.value = function(obj, path, value) {
var node = this.nodes(obj, path).shift();
if (!node) return this._vivify(obj, path, value);
var key = node.path.slice(-1).shift();
var parent = this.parent(obj, this.stringify(node.path));
var parent = _get_value_by_path(obj, node.path, 1);
parent[key] = value;
}
return this.query(obj, this.stringify(path), 1).shift();
Expand Down Expand Up @@ -240,6 +257,49 @@ function _is_string(obj) {
return Object.prototype.toString.call(obj) == '[object String]';
}

function _get_value_by_path(obj, path, skip) {
skip = skip || 0;
for (var i = 1; i < path.length - skip; ++i)
obj = obj[path[i]];
return obj;
}

var _NodeWrapper = function(root, path, value) {
this.root = root;
this.path = path;
this.value = value;
};

_NodeWrapper.prototype.parent = function(skip) {
skip = skip + 1 || 1;
if (skip > this.path.length)
throw Error("couldn't skip passed root object");
var parentPath = this.path.slice(0, -skip);
var parentValue = _get_value_by_path(this.root, parentPath);
return new _NodeWrapper(this.root, parentPath, parentValue);
};

_NodeWrapper.prototype.key = function() {
return this.path[this.path.length - 1];
};

_NodeWrapper.prototype.update = function(value) {
this.parent()[this.key()] = value;
};

_NodeWrapper.prototype.remove = function() {
var parent = this.parent();
var key = this.key();
var value = parent[key];

if (Array.isArray(parent))
Array.prototype.splice.call(parent, key, 1);
else
delete parent[key];

return value;
};

JSONPath.Handlers = Handlers;
JSONPath.Parser = Parser;

Expand Down