histarray – An Experiment with Getters and Setters

While doing an experiment with Getters and Setters I whipped together a small lib for a versioning array, the histarray. So far tested only in Firefox 3, it utilizes __defineGetter__(someNumber, function(){}) to return an element from an internal array (the history). Its push method will either overwrite the current revision if it is not the last, or push a new revision into the history.

A Setter is also defined, so no matter where in the time line you currently are, that element will be updated.

function histarray()
{
	for(var i = 0; i < arguments.length; i++)
		this._add(arguments[i], i);

	this.__defineSetter__('revision', function(v)
	{
		return (v > this.length) ?
			this.revision :
			this._revision = v;
	});
	this.__defineGetter__('revision', function()
	{
		return this._revision;
	});
	this.__defineGetter__('prev', function()
	{
		return (this.revision != 0) ?
			(function(self)
			{
				var a = Array();
				for(var i in self._elements)
					a.push(self._elements[i][self.revision - 1]);
				return a;
			})(this) :
			null;
	});
	this.__defineGetter__('nxt', function()
	{
		return (this.revision < this.revisions) ?
			(function(self)
			{
				var a = Array();
				for(var i in self._elements)
					a.push(self._elements[i][self.revision + 1]);
				return a;
			})(this) :
			null;
	});
};

histarray.prototype =
{
	length: 0,
	revisions: 0,
	_elements: Array(),
	_revision: 0,
	_add: function(e, i)
	{
		this._elements[i] = (function(e,self)
		{
			var a = Array();
			for(var i = 0; i < self.revisions; i++)
				a.push(null);
			a.push(e);
			return a;
		})(e,this);
		this.__defineGetter__(i, function()
		{
			return this._elements[i][this.revision];
		});

		this.__defineSetter__(i, function(v)
		{
			return this._elements[i][this.revision] = v;
		});
		this.length = i + 1;
	},
	// push csv or a single array
	push: function()
	{
		if(typeof arguments[0] == 'object')
			this.push.apply(this, arguments[0]);
		else
		{
			if(this.revision == this.revisions)
			{
				this.revision++;
				this.revisions++;
			}
			for(var i = 0; i < arguments.length; i++)
			{
				if(arguments[i] == null) continue;

				if(this._elements[i])
					this._elements[i][this.revision] = arguments[i];
				else
					this._add(arguments[i], i);
			}
		}
		return this.length;
	}
};
// creates a new histarray.
var ha = new histarray('e1', 'e2', 'e3');
// this is now just like a normal array, ha[0] == 'e1'
// now using push does not add a new element, but instead adds a new revision
ha.push('e1v2', 'e2v2', 'e3v2')
// now ha[0] == 'e1v2' and ha.revision == 1 and ha.prev[0] == 'e1'
// if we change the revision back to 0
ha.revision = 0;
// ha[0] == 'e1', ha.nxt[0] == 'e1v2'
// with the revision still at 0, we can update an old element
ha[0] = 'e1a';
// this just changes the first revision of the first element.

Not certain is will be useful for anything in particular. But I still have a couple ideas to add, and am interested in hear what people have to say about this.

More Histarray

I am not sure if it is even useful for anything. But I am having fun dorking on it.

New Features.
added pop() method
added splice() method
unified adding and removing for more accurate revision and length

New Home
I have been using github for a little now, and have put this up as my first public repo.

*Notes
There is one big, probably unresolvable issue. You have to push in new elements.

 var a = Array('one', 'two', 'three');
 a[12] = 12;
 // this would result in an array with a length of 13
 // with 3-12 undefined.

The way the getters and setters are setup, unless I where to define ervythin’ there would be nothing waiting to assign an element to the internal array.

Next Steps (if any)
Implement standard array features
Decide if push should be like an array push and create a new method for historical pushes.

Last up
I also finally got around to figuring out unit testing. It is really easy to use, not sure why I have not been using it longer. I have 19(?) tests right now, and they are in /test in the repo.

Leave a Comment