Javascript: sort objects - javascript

function Player() {
var score;
this.getScore = function() { return score; }
this.setScore = function(sc) { score = sc; }
}
function compare(playerA, playerB) {
return playerA.getScore() - playerB.getScore();
}
var players = [];
players['player1'] = new Player();
players['player2'] = new Player();
Array(players).sort(compare);
I have code that is similar to the above. When I step through the code with a debugger, the compare function never gets called and the array isn't sorted. I'm not sure what's wrong with my code?

It's not sorting because you have specified the keys that the variables within the array belong on. Sorting will only move the objects on integer-valued keys. You should see your sorting work if you create your array as follow:
var players = [new Player(), new Player()];
though, of course, it won't be very effective since you have neither a score on which to sort or a method of identifying them. This'll do it:
function Player(name, score) {
this.getName = function() { return name; }
this.getScore = function() { return score; }
this.setScore = function(sc) { score = sc; }
}
function comparePlayers(playerA, playerB) {
return playerA.getScore() - playerB.getScore();
}
var playerA = new Player('Paul', 10);
var playerB = new Player('Lucas', 5);
var playerC = new Player('William', 7);
var players = [playerA, playerB, playerC];
for (var i = 0; i < players.length; i++)
alert(players[i].getName() + ' - ' + players[i].getScore());
players.sort(comparePlayers);
for (var i = 0; i < players.length; i++)
alert(players[i].getName() + ' - ' + players[i].getScore());
Hope that helps.

The main problem lies in this line:
Array(players).sort(compare);
Array(something) makes an array with something as its element.
console.log(Array(players)); //[[player1, player2]]
Use numeric indexed array instead of using object like array as in players['player1']
Run the following code (replace console.log with alert if you don't have Firebug).
function Player() {
var score;
//return this.score - else it returns undefined
this.getScore = function() { return this.score; }
this.setScore = function(sc) { this.score = sc; }
}
function compare(playerA, playerB) {
console.log("called " + playerA.getScore() + " " + playerB.score);
//compare method should return 0 if equal, 1 if a > b and -1 if a < b
return (playerA.getScore() == playerB.getScore()) ? 0
: ((playerA.getScore() > playerB.getScore()) ? 1 : -1);
}
var players = [];
players[0] = new Player();
players[1] = new Player();
players[2] = new Player();
players[3] = new Player();
players[0].setScore(9);
players[1].score = 14;
players[2].score = 11;
players[3].score = 10;
players.sort(compare);
console.log(players);//prints sorted array

It's probably because you don't have any "array values" inside your array - textual indexes are not regarded as array values but as object propertiest (arrays are "objects in disguise" in javascript). You can add as many properties to any object but array specific methods like sort take only "real" array members as their parameteres (i.e. only with numerical indexes)
var arr = new Array()
arr[0] = 1
arr[1] = 2
arr["textual_index"] = 3
alert(arr.length);
The last line alerts "2" not "3" since there are only two values with numeric indexes.

you can also use like below:
var a = [];
a.push(obj1);
a.push(obj2);
a.sort(compare);
so you can use push method rather than an integer index

Related

Javascript foreach not executing for all objects

I currently have an object that adds itself to an array whenever a new one is created. Eventually, I want to remove all of the references in the array so I can add new ones.
I've created an object method (this.removeFromArray()) that looks for itself in the array and splices itself out. removeAll() runs a for loop that makes each object in the array run removeFromArray(), so I expect that when I try to read out the items in the array, I should get nothing.
Instead, depending on the amount of objects created, I get one or two left behind. How can I fix this and have all objects in the array cleared out?
var objArray = [];
function obj(name) {
objArray.push(this);
console.log("Created "+name);
this.name = name;
this.removeFromArray = function() {
objArray.splice(
objArray.findIndex(function(e) {
return e == this;
}),
1
);
}
}
function removeAll() {
for (var i = 0; i <= objArray.length - 1; i++) {
objArray[i].removeFromArray();
}
}
var foo = new obj("foo");
var bar = new obj("bar");
var cat = new obj("cat");
var dog = new obj("dog");
var bird = new obj("bird");
removeAll();
for (var i = 0; i <= objArray.length-1; i++) { //Check the values in the array for leftovers
console.log(objArray[i].name);
}
//Expected nothing in the console but the creation messages, got foo and bar instead
If you want to simply delete all the created object, edit removeAll() function like below:
Note that you have to create a variable for objArray.length, not directly put the objArray.length to for() loop.
function removeAll() {
var len = objArray.length;
for (var i = 0; i <= len - 1; i++) {
objArray.splice(0,1);
}
}
better way to achieve this would be to utilize inheritance through prototype. it is better than creating a function inside the constructor object.
var objArray = [];
function Obj(name) {
this.name = name;
objArray.push(this);
}
Obj.prototype.removeFromArray = function() {
var i = -1,
len = objArray.length,
removed = null;
while (++i < len) {
if (objArray[i] === this) {
removed = objArray.splice(i, 1);
removed = null; //nullify to free memory, though not that necessary
break;
}
}
};
Obj.prototype.removeAll = function() {
var len = objArray.length,
removed = null;
//note that i started from the last item to remove to avoid index out of range error
while (--len >= 0) {
removed = objArray.splice(len, 1);
removed = null; //nullify to free memory, though not that necessary
}
};

How to do I unshift/shift single value and multiple values using custom methods?

I have prototypes to recreate how array methods work, pop/push/shift/etc, and I would like to extend the functionality to do the following:
Push/Pop/shift/unshift multiple values
array.push(0);
array.push(1);
array.push(2);
expect(array.pop()).to.be(2);
expect(array.pop()).to.be(1);
expect(array.pop()).to.be(0);
Push/Pop/unshift/etc single values
array.push(0);
array.push(1);
expect([0,1]);
array.pop(1);
expect([0]);
My assumption is that I would need a global array variable to store the elements. Is that the right?
Here is my code:
var mainArray = []; // array no longer destroyed after fn() runs
function YourArray(value) {
this.arr = mainArray; // looks to global for elements | function?
this.index = 0;
var l = mainArray.length;
if(this.arr === 'undefined')
mainArray += value; // add value if array is empty
else
for(var i = 0; i < l ; i++) // check array length
mainArray += mainArray[i] = value; // create array index & val
return this.arr;
}
YourArray.prototype.push = function( value ) {
this.arr[ this.index++ ] = value;
return this;
};
YourArray.prototype.pop = function( value ) {
this.arr[ this.index-- ] = value;
return this;
};
var arr = new YourArray();
arr.push(2);
console.log(mainArray);
My assumption is that I would need a global array variable to store
the elements. Is that the right?
No. That is not right.
You want each array object to have its own, independent set of data. Otherwise, how can you have multiple arrays in your program?
function YourArray(value) {
this.arr = []; // This is the data belonging to this instance.
this.index = 0;
if(typeof(value) != 'undefined')) {
this.arr = [value];
this.index = 1;
}
}
////////////////////////////////////
// Add prototype methods here
///////////////////////////////////
var array1 = new YourArray();
var array2 = new YourArray();
array1.push(2);
array1.push(4);
array2.push(3);
array2.push(9);
// Demonstrate that the values of one array
// are unaffected by the values of a different array
expect(array1.pop()).to.be(4);
expect(array2.pop()).to.be(9);
It's a bit late for this party, admitted but it nagged me. Is there no easy (for some larger values of "easy") way to do it in one global array?
The standard array functions work as in the following rough(!) sketch:
function AnotherArray() {
this.arr = [];
// points to end of array
this.index = 0;
if(arguments.length > 0) {
for(var i=0;i<arguments.length;i++){
// adapt if you want deep copies of objects
// and/or take a given array's elements as
// individual elements
this.arr[i] = arguments[i];
this.index++;
}
}
}
AnotherArray.prototype.push = function() {
// checks and balances ommitted
for(var i=0;i<arguments.length;i++){
this.arr[ this.index++ ] = arguments[i];
}
return this;
};
AnotherArray.prototype.pop = function() {
this.index--;
return this;
};
AnotherArray.prototype.unshift = function() {
// checks and balances ommitted
var tmp = [];
var alen = arguments.length;
for(var i=0;i<this.index;i++){
tmp[i] = this.arr[i];
}
for(var i=0;i<alen;i++){
this.arr[i] = arguments[i];
this.index++;
}
for(var i=0;i<tmp.length + alen;i++){
this.arr[i + alen] = tmp[i];
}
return this;
};
AnotherArray.prototype.shift = function() {
var tmp = [];
for(var i=1;i<this.index;i++){
tmp[i - 1] = this.arr[i];
}
this.arr = tmp;
this.index--;
return this;
};
AnotherArray.prototype.isAnotherArray = function() {
return true;
}
AnotherArray.prototype.clear = function() {
this.arr = [];
this.index = 0;
}
AnotherArray.prototype.fill = function(value,length) {
var len = 0;
if(arguments.length > 1)
len = length;
for(var i=0;i<this.index + len;i++){
this.arr[i] = value;
}
if(len != 0)
this.index += len;
return this;
}
// to simplify this example
AnotherArray.prototype.toString = function() {
var delimiter = arguments.length > 0 ? arguments[0] : ",";
var output = "";
for(var i=0;i<this.index;i++){
output += this.arr[i];
if(i < this.index - 1)
output += delimiter;
}
return output;
}
var yaa = new AnotherArray(1,2,3);
yaa.toString(); // 1,2,3
yaa.push(4,5,6).toString(); // 1,2,3,4,5,6
yaa.pop().toString(); // 1,2,3,4,5
yaa.unshift(-1,0).toString(); // -1,0,1,2,3,4,5
yaa.shift().toString(); // 0,1,2,3,4,5
var yaa2 = new AnotherArray();
yaa2.fill(1,10).toString(); // 1,1,1,1,1,1,1,1,1,1
Quite simple and forward and took only about 20 minutes to write (yes, I'm a slow typist). I would exchange the native JavaScript array in this.arr with a double-linked list if the content can be arbitrary JavaScript objects which would make shift and unshift a bit less memory hungry but that is obviously more complex and slower, too.
But to the main problem, the global array. If we want to use several individual chunks of the same array we need to have information about the starts and ends of the individual parts. Example:
var globalArray = [];
var globalIndex = [[0,0]];
function YetAnotherArry(){
// starts at the end of the last one
this.start = globalIndex[globalIndex.length-1][1];
this.index = this.start;
// position of the information in the global index
this.pos = globalIndex.length;
globalIndex[globalIndex.length] = [this.start,this.index];
}
So far, so well. We can handle the first array without any problems. We can even make a second one but the moment the first one wants to expand its array we get in trouble: there is no space for that. The start of the second array is the end of the first one, without any gap.
One simple solution is to use an array of arrays
globalArray = [
["first subarray"],
["second subarray"],
...
];
We can than reuse what we already wrote in that case
var globalArray = [];
function YetAnotherArray(){
// open a new array
globalArray[globalArray.length] = [];
// point to that array
this.arr = globalArray[globalArray.length - 1];
this.index = 0;
}
YetAnotherArray.prototype.push = function() {
for(var i=0;i<arguments.length;i++){
this.arr[ this.index++ ] = arguments[i];
}
return this;
};
// and so on
But for every new YetAnotherArray you add another array to the global array pool and every array you abandon is still there and uses memory. You need to manage your arrays and delete every YetAnotherArray you don't need anymore and you have to delete it fully to allow the GC to do its thing.
That will leave nothing but gaps in the global array. You can leave it as it is but if you want to use and delete thousands you are left with a very sparse global array at the end. Or you can clean up. Problem:
var globalArray = [];
function YetAnotherArray(){
// add a new subarray to the end of the global array
globalArray[globalArray.length] = [];
this.arr = globalArray[globalArray.length - 1];
this.index = 0;
this.pos = globalArray.length - 1;
}
YetAnotherArray.prototype.push = function() {
for(var i=0;i<arguments.length;i++){
this.arr[ this.index++ ] = arguments[i];
}
return this;
};
YetAnotherArray.prototype.toString = function() {
var delimiter = arguments.length > 0 ? arguments[0] : ",";
var output = "";
for(var i=0;i<this.index;i++){
output += this.arr[i];
if(i < this.index - 1)
output += delimiter;
}
return output;
}
// we need a method to delete an instance
YetAnotherArray.prototype.clear = function() {
globalArray[this.pos] = null;
this.arr = null;
this.index = null;
};
YetAnotherArray.delete = function(arr){
arr.clear();
delete(arr);
};
// probably won't work, just a hint in case of asynch. use
var mutex = false;
YetAnotherArray.gc = function() {
var glen, indexof, next_index, sub_len;
indexof = function(arr,start){
for(var i = start;i<arr.length;i++){
if (arr[i] == null || arr[i] == undefined)
return i;
}
return -1;
};
mutex = true;
glen = globalArray.length;
sublen = 0;
for(var i = 0;i<glen;i++){
if(globalArray[i] == null || globalArray[i] == undefined){
next_index = indexof(globalArray,i);
if(next_index == -1){
break;
}
else {
globalArray[i] = globalArray[next_index + 1];
globalArray[next_index + 1] = null;
sublen++;
}
}
}
globalArray.length -= sublen - 1;
mutex = false;
};
var yaa_1 = new YetAnotherArray();
var yaa_2 = new YetAnotherArray();
var yaa_3 = new YetAnotherArray();
var yaa_4 = new YetAnotherArray();
yaa_1.push(1,2,3,4,5,6,7,8,9).toString(); // 1,2,3,4,5,6,7,8,9
yaa_2.push(11,12,13,14,15,16).toString(); // 11,12,13,14,15,16
yaa_3.push(21,22,23,24,25,26,27,28,29).toString();// 21,22,23,24,25,26,27,28,29
yaa_4.push(311,312,313,314,315,316).toString(); // 311,312,313,314,315,316
globalArray.join("\n");
/*
1,2,3,4,5,6,7,8,9
11,12,13,14,15,16
21,22,23,24,25,26,27,28,29
311,312,313,314,315,316
*/
YetAnotherArray.delete(yaa_2);
globalArray.join("\n");
/*
1,2,3,4,5,6,7,8,9
21,22,23,24,25,26,27,28,29
311,312,313,314,315,316
*/
YetAnotherArray.gc();
globalArray.join("\n");
/*
1,2,3,4,5,6,7,8,9
21,22,23,24,25,26,27,28,29
311,312,313,314,315,316
*/
But, as you might have guessed already: it doesn't work.
YetAnotherArray.delete(yaa_3); // yaa_3 was 21,22,23,24,25,26,27,28,29
globalArray.join("\n");
/*
1,2,3,4,5,6,7,8,9
21,22,23,24,25,26,27,28,29
*/
We would need another array to keep all positions. Actual implementation as an exercise for the reader but if you want to implement a JavaScript like array, that is for arbitrary content you really, really, really should use a doubly-linked list. Or a b-tree. A b+-tree maybe?
Oh, btw: yes, you can do it quite easily with a {key:value} object, but that would have squeezed all the fun out of the job, wouldn't it? ;-)

Find index of object in an array of prototypes Javascript

I am creating an app using jQuery mobile and need to get the index of an object in an array of prototypes.
The object, call team, is as follows:
var team = function (teamname, colour, rss_url, twitter_url, website, coords) {
this.teamname = teamname;
this.colour = colour;
this.rss_url = rss_url;
this.twitter_url = twitter_url;
this.website = website;
this.location = coords;
};
And the array itself looks like:
var teamlist = [32];
teamlist[0] = new team("Aberdeen","#C01A1A","http://www.football365.com/aberdeen/rss", "https://twitter.com/aberdeenfc","http://www.afc.co.uk/","57�09?33?N�2�05?20?W");
teamlist[1] = new team("Celtic","#C01A1A","http://www.football365.com/aberdeen/rss", "https://twitter.com/aberdeenfc","http://www.afc.co.uk/","57�09?33?N�2�05?20?W");
teamlist[2] = new team("Dundee","#C01A1A","http://www.football365.com/aberdeen/rss", "https://twitter.com/aberdeenfc","http://www.afc.co.uk/","57�09?33?N�2�05?20?W");
teamlist[3] = new team("Dundee United","#C01A1A","http://www.football365.com/aberdeen/rss", "https://twitter.com/aberdeenfc","http://www.afc.co.uk/","57�09?33?N�2�05?20?W");
teamlist[4] = new team("Hamilton Academical","#C01A1A","http://www.football365.com/aberdeen/rss", "https://twitter.com/aberdeenfc","http://www.afc.co.uk/","57�09?33?N�2�05?20?W");
teamlist[5] = new team("Inverness Caledonian Thistle","#C01A1A","http://www.football365.com/aberdeen/rss", "https://twitter.com/aberdeenfc","http://www.afc.co.uk/","57�09?33?N�2�05?20?W");`
I need to be able to get the index of an object based on teamname. Ihad thought something along the lines of
var a = teamlist.indexOf(teamname: "Aberdeen");
That however, is obviusly not working.
Any suggestions are welcome - thanks in advance! :)
Simple enough. You can use Array.prototype.some, have the index declared as a variable in lexical scope, and change it when a match occurs. Then return the index. Something like this:
var data = [
{x: '1'},
{x: '2'},
{x: '3'},
{x: '4'}
]; // sample data
function findIndex (num) {
// num is just the number corresponding to the object
// in data array that we have to find
var index = -1; // default value, in case no element is found
data.some(function (el, i){
if (el.x === num) {
index = i;
return true;
}
}); // some will stop iterating the moment we return true
return index;
}
console.log(findIndex('3'));
Hope that helps!
Use this simple index search by value algorithm.
function index_by_value(teamlist, teamname) {
index = 0
for (var i = 0; i < teamlist.length; i++) {
if (teamlist[i].teamname == teamname) {
index = i;
}
}
return index;
}
index = index_by_value(teamlist, "Aberdeen"); // an example
Keep in mind though that if there are multiple objects in the list with the same teamname, it will return the last one's index. If it does not exist, the function will return 0.
You can use filter method:
var a = teamlist.filter(function(team) {
return team.teamname = "Aberdeen";
});
It will produce new array of the teams object with the name of "Aberdeen". If you expect only one team the you need to take the first element from this array: a[0].
function getIndex(teamlist, teamname){
for(var i = 0; i<teamlist.length; i++){
if(teamlist[i].teamname == teamname){
return i; // if found index will return
}
}
return -1; // if not found -1 will return
}
var a = getIndex(teamlist,"Aberdeen"); // will give a as 0

javascript max call stack exceeded explanation

var MultiArray = (function(){
var MultiArray = function(param){
var i = param.array.length;
while(i--){
param.array[i] = MultiArray({
array: new Array(dims[0]),
dims: dims.splice(1,dims.length-1)
});
}
return param.array
};
return function(){
var length = arguments.length - 1;
array = new Array(arguments[0]),
dims = [length];
for(var i = 0; i < length; i++){
dims[i] = arguments[i+1];
}
MultiArray({
array: array,
dims: dims
});
return array;
};
})();
I'm trying to write a function that will create multidimensional arrays so var mdArray = MultiArray(3,3,3); would create a 3D array of 3x3x3 elements. But I get Max call stack size exceeded when trying this method, is there a better way of making a multidimensional array function like this?
In your inner MultiArray, you always have a param.array with 1 items, which makes a endless recursion.
var MultiArray = (function() {
var MultiArray = function(param) {
var myDims, i = param.array.length;
if(param.dims.length === 0){
return;
}
while(i--) {
myDims = param.dims.slice(0);
myDims = myDims.splice(1, myDims.length - 1);
param.array[i] = new Array(param.dims[0]);
MultiArray({
array : param.array[i],
dims : myDims
});
}
};
return function() {
var length = arguments.length - 1, array = new Array(arguments[0]), dims = [length];
for(var i = 0; i < length; i++) {
dims[i] = arguments[i + 1];
}
MultiArray({
array : array,
dims : dims
});
return array;
};
})();
The code above is correct the original code was simply wrong causing an infinite recursive loop, now it works as intended, var mdArray = MultiArray(2,3,4,5); creates an array with dimensions 2x3x4x5. The recursive function now returns straight away when the dims array in the argument has zero length.

Loop through two Array in JavaScript and Constructing a function

I have Two Arrays in Javascript as shown below :
Array one = new Array();
one.push(20061001);
one.push(20061002);
one.push(20061003);
one.push(20061120);
Array two = new Array();
two.push(3.0);
two.push(3.1);
two.push(3.2);
two.push(3.3);
Now Some how i need to loop through this Array and construct a function as shown
function NoisyData() {
return "" +
"Date,A\n" +
"20061001,3.0\n" +
"20061002,3.1\n" +
"20061003,3.2\n" +
"20061120,4.0\n" ;
}
Please help me as how to do this ??
How about this?
var one = new Array();
one.push(20061001);
one.push(20061002);
one.push(20061003);
one.push(20061120);
var two = new Array();
two.push('3.0');
two.push('3.1');
two.push('3.2');
two.push('3.3');
function NoisyData() {
var result = "Date,A\n";
for(var i = 0; i < one.length;i++){
result += one[i] + "," + two[i] + "\n";
}
return result;
}
alert(NoisyData());
The faster way for long array is :
var one = new Array();
one.push(20061001);
one.push(20061002);
one.push(20061003);
one.push(20061120);
var two = new Array();
two.push(3.0);
two.push(3.1);
two.push(3.2);
two.push(3.3);
function NoisyData() {
var ret = [];
ret.push("Date,A");
for (var i=0;i<one.length;i++){
ret.push(one[i]+','+two[i]);
}
return ret.join('\n');
}
alert(NoisyData());
Your code can be a lot shorter. You can't type variables (like Array one) in javascript. To declare an Array most of the time an Array literal is sufficient.
If your arrays have the same length, you can use the code hereby to combine them into the string you need:
var one = [20061001,20061002,20061003,20061120]
, two = [3.0,3.1,3.2,3.3]
, combine = function(a1,a2){
var i = -1, len = a1.length, res = ['Date,A'];
while(++i < len){
res.push(a1[i]+','+a2[i].toPrecision(2));
}
return res.join('\n');
}(one,two);
Try it # http://jsfiddle.net/KooiInc/jdn6U/
You mean
function NoisyData() {
var txt = "Date,A\n"
for (var i=0, n=one.length;i<n;i++) {
txt += one[i]+","+two[i]+"\n"
}
return txt
}
UPDATE based on KooiInc's posts:
<script>
var one = [20061001,20061002,20061003,20061120]
, two = [3.0,3.1,3.2,3.3]
, combined = function(res,two){
var i = one.length;
while(i--){
res[i]+=','+two[i].toPrecision(2);
}
res.splice(0,0,'Date,A');
return res.join('\n')
}(one.slice(0),two);
alert(combined);
</script>
Instead of one.slice(0) one.clone() can be implemented as
Array.prototype.clone = function() { return this.slice(0); }
or just pass one itself instead if it is OK to modify the original array

Categories

Resources