Skip to content

Instantly share code, notes, and snippets.

@jasuperior
Created December 26, 2017 15:24
Show Gist options
  • Save jasuperior/807ace24838ca5b45fe42d8e5d36f420 to your computer and use it in GitHub Desktop.
Save jasuperior/807ace24838ca5b45fe42d8e5d36f420 to your computer and use it in GitHub Desktop.
Rrray(Relative Array): A concept using ES6 Proxies to construct a type of array whose values keep track of their sibling relationships. Also allows for circular/negative indexing.
class Rrray extends Array {
constructor(){
super(...arguments);
this.index = {}
return new Proxy(this, this.handler);
}
get handler () {
let $self = this;
return {
get(target, prop, proxy){
let Num = target.isNum(prop);
if(typeof prop == "symbol"){
if(typeof target[prop] == "function")
return target[prop].bind(target)
return target[prop]
}
let reg = /(\$|_)(\d+)/;
let indexes = prop.match(reg);
if(indexes){
if(indexes[1] == "$"){
return proxy[indexes[2]]
}else if (indexes[1]=="_"){
return proxy["-"+indexes[2]]
}
}
/*Every alternation results in a call to this.makeValue, which creates a synthetic Rrray with a default .value
property which represents the value of the provided index, and a relative "space" which is an Rrray
which represents all indexes surrounding your value*/
if(Num||Num===0){
if(Num > -1){
let left = target.slice(0,Num)||[]; //slice the left values to the left and right of target value.
let right = target.slice(Num+1)||[];
return target.makeValue(
target[Num], //supply the value of the index
new Rrray(...[target[Num],...right,...left]), //a rearranged Rrray for the space
Num //the index of the value
);
}else{
Num = target.length + Num;
let left = target.slice(0,Num)||[];
let right = target.slice(Num+1)||[];
return target.makeValue(
target[Num],
new Rrray(...[target[Num],...right,...left]),
Num
);
}
}else{
if(typeof target[prop] == "function"){
return target[prop].bind(target);
}
let index = target.index[prop];
if(index||index===0){
if(index > -1){
let left = target.slice(0,index)||[];
let right = target.slice(index+1)||[];
return target.makeValue(
target[index],
new Rrray(...[target[index],...right,...left]),
index
);
} else{
index = target.length + index;
let left = target.slice(0,index);
let right = target.slice(index+1);
return target.makeValue(
target[index],
new Rrray(...[target[index],...right,...left]),
index
)
}
}
}
},
set( target, prop, value ) {
let $value = target.isNum(value), $prop = target.isNum(prop);
if((!$prop && $prop !== 0)){
if(!$value && $value !== 0){
target.push(value);
target.index[prop] = target.length-1;
}
else target.index[prop] = value;
}
if($prop || $prop === 0){
target[$prop>-1?$prop:target.length+$prop] = value;
}
return true;
},
getPrototypeOf (){
return Rrray.prototype;
}
}
}
makeValue( value, space = new Rrray(), index) {
/* creates a value type which when used as a primitive expresses the value of the slot,
if accessing a property or index, it will source from the space array (the constructed Rrray of relative values) */
let $self = this;
return new Proxy({value, space, index}, {
get( target, prop){
if(prop == "value") return target.value;
if(prop == "index") return target.index;
let value;
if(prop == Symbol.toPrimitive) return ()=> (target.value && target.value.valueOf());
if(typeof target.space[prop] == "function") return target.space[prop].bind(target.space);
return target.space[prop]?target.space[prop]:target.value?target.value[prop]:undefined;
},
set(target, prop, value) {
let num = $self.isNum(prop);
if(num === 0){
target.value = value;
prop = num;
}
target.space[prop] = value;
return true;
},
getPrototypeOf (){
return Rrray.prototype;
}
})
}
isNum ( prop ) {
if(typeof prop == "symbol") return;
return !isNaN(prop)&&parseInt(prop);
}
set(prop, value) {
}
}
var arr = new Rrray(0,1,2,3,4,5,6,7,8,9);
console.log(arr[0] == 0) //true
console.log(arr[-1] == 9)//true
console.log(arr[3][-5] == 8) //true
//All new properties, are pushed to the back of the array, and given an index. Thus, all properties have access to its siblings
//as well
arr.person = "Susan";
console.log(arr.person == "Susan");
console.log(arr.person.index == 10); //all values have an index property which is it's index in the array.
console.log(arr.person[-1] == 9);
arr.person_number = -2; //setting a property to a value will point the property to that index, rather than setting it to the value;
console.log(arr.person_number == 9, +arr.person_number == +arr.person[-1]); // "+" symbol added to force number typing. (Stupid js equivilency).
console.log(arr.person_number.index == 9);
//Also there is a special way to call indexes as normal property values by prefixing _ before the number for negative values
//and $ for positive values
//This is especially useful when having to destructure for a function, and needing to know the relative values of a given value
function test ( { _1, $1, $5 } ){
console.log( _1 + $1 + $5 )
}
test(arr); //Susan15
test(arr.person) //13;
@edzillion
Copy link

@jasuperior
Copy link
Author

jasuperior commented Nov 27, 2020

Hey thanks this helped me with a problem I was having https://stackoverflow.com/questions/64996534/is-is-possible-to-pass-this-context-into-an-es6-proxy-handler

@edzillion
Hehehe... no problem. I know it’s not necessarily the cleanest solution, but at the time it was the easiest way I could come up with to attack I dance variables on a proxy.

Now, I would probably handle this with a function. Pass in the values and apply it to an object and return a proxy over the created object.

It gets rid of the confusion that arises when class validation. The code would have you believe that the object your returned after a new Rray is an Rray, but it’s actually a proxy, so it will fail all validation.

With the functional method, there return value always determines the type of the function... so it’s all compliant. Even one better, it plays nicely with typescript, returning a Proxy<UnderlyingClass> which is more coherent and descriptive of the object you have.

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