Update 2
I added a weight lookup to the sort function, which increased the performance by about 100% as well as the stability, as the previous sort function didn't consider all types, and as 1 == "1"
the result depends on the initial order of the Array, as @Esailija points out.
The intent of the question is to improve this Answer of mine, I liked the question and since it got accepted and I felt like there is some performance to squeeze out of the sort function. I asked this question here since I hadn't many clues left where to start.
Maybe this makes things clearer as well
Update
I rephrased the complete question, as many people stated I was not specific enough, I did my best to specify what I mean. Also, I rewrote the sort
function to better express the intent of the question.
Let arrayPrev be an Array (A) ,where A consists of 0 to n Elements' (E)
- Let an Element either be
- of a Primitive type
- boolean, string, number, undefined, null
- a Reference to an Object O, where O.type = [object Object] and O can consist of
- 0 to n Properties P, where P is defined like Element plus
- an circular Reference to any P in O
- Where any O can be contained 1 to n times. In the sense of GetReferencedName(E1) === GetReferencedName(E2)...
- 0 to n Properties P, where P is defined like Element plus
- a Reference to an O, where O.type = [object Array] and O is defined like A
- a circular Reference to any E in A
- of a Primitive type
- Let an Element either be
- Let arrayCurr be an Array of the same length as arrayPrev
Illustrated in the following example
var x = {
a:"A Property",
x:"24th Property",
obj: {
a: "A Property"
},
prim : 1,
}
x.obj.circ = x;
var y = {
a:"A Property",
x:"24th Property",
obj: {
a: "A Property"
},
prim : 1,
}
y.obj.circ = y;
var z = {};
var a = [x,x,x,null,undefined,1,y,"2",x,z]
var b = [z,x,x,y,undefined,1,null,"2",x,x]
console.log (sort(a),sort(b,a))
The Question is, how can I efficiently sort an Array B, such that any Reference to an Object or value of a Primitive, shares the exact same Position as in a Previously, by the same compareFunction, sorted, Array A.
Like the above example
Where the resulting array shall fall under the rules.
- Let arrayPrev contain the Elements' of
a
and arrayCurr contain the Elements' ofb
- Let arrayPrev be sorted by a CompareFunction C.
- Let arrayCurr be sorted by the same C.
- Let the result of sorting arrayCur be such, that when accessing an E in arrayCur at Position n, let n e.g be 5
- if type of E is Object GetReferencedName(arrayCurr[n]) === GetReferencedName(arrayPrev[n])
- if type of E is Primitive GetValue(arrayCurr[n]) === GetValue(arrayPrev[n])
- i.e
b[n] === a[n]
e.gb[5] === a[5]
- Meaning all Elements should be grouped by type, and in this sorted by value.
- Where any call to a Function F in C shall be at least implemented before ES5, such that compatibility is given without the need of any shim.
- Let the result of sorting arrayCur be such, that when accessing an E in arrayCur at Position n, let n e.g be 5
My current approach is to Mark the Objects in arrayPrev to sort them accordingly in arrayCurr and later delete the Marker again. But that seems rather not that efficient.
Heres the current sort
function used.
function sort3 (curr,prev) {
var weight = {
"[object Undefined]":6,
"[object Object]":5,
"[object Null]":4,
"[object String]":3,
"[object Number]":2,
"[object Boolean]":1
}
if (prev) { //mark the objects
for (var i = prev.length,j,t;i>0;i--) {
t = typeof (j = prev[i]);
if (j != null && t === "object") {
j._pos = i;
} else if (t !== "object" && t != "undefined" ) break;
}
}
curr.sort (sorter);
if (prev) {
for (var k = prev.length,l,t;k>0;k--) {
t = typeof (l = prev[k]);
if (t === "object" && l != null) {
delete l._pos;
} else if (t !== "object" && t != "undefined" ) break;
}
}
return curr;
function sorter (a,b) {
var tStr = Object.prototype.toString
var types = [tStr.call(a),tStr.call(b)]
var ret = [0,0];
if (types[0] === types[1] && types[0] === "[object Object]") {
if (prev) return a._pos - b._pos
else {
return a === b ? 0 : 1;
}
} else if (types [0] !== types [1]){
return weight[types[0]] - weight[types[1]]
}
return a>b?1:a<b?-1:0;
}
}
Heres a Fiddle as well as a JSPerf (feel free to add your snippet)
And the old Fiddle