JavaScript data structure : key/value with key access from the value - javascript

I need a data structure to store several JavaScript objects, and to be able to access them with a string id (get/set/delete operations).
Here is an example of the items I need to store :
var Player = function(name) {
this.name = name;
this.x = 0;
this.y = 0;
this.toString = function() {
return 'player : ' + this.name + ' at ' + this.x + ', ' + this.y;
};
}
I would like to store players in a data structure and to be able to get/set/delete them by their name, like players.get('Bob') to get the player with Bob as name.
At first, I thought I could use a map with the name as key (I'm using Dict from collectionsjs). But then I can't access the name from the methods of the item (toString in my example).
I could use a regular Array, keep the name attribute and implement my own get/set/delete methods, however I would rather use a reliable data structure but I can't find it.
Thanks in advance :]

A Javascript object would work.
var players = [];
players[0] = {"name":"Bob", "age":1};
players[1] = {"name":"John", "age":4};
for (var i in players) {
if (players[i].name == "Bob") {
alert("Bob is " + players[i].age);
}
}
EDIT:
var players = [];
players[0] = {"name":"Bob", "age":1};
players[1] = {"name":"John", "age":4};
players.forEach(function(player){
if (player.name == "Bob") {
alert("Bob is " + player.age);
}
});

var Players = function(){
this.players = [];
this.add = function(player){
this.players.push(player);
}
this.delete = function(name){
for(var i=0;i<this.players.length;i++)
if(this.players[i].name==name)
{
var f = this.players.slice(i+1,this.players.length+1);
this.players = this.players.slice(0,i).concat(f);
return;
}
}
this.set = function(name,player){
for(var i=0;i<this.players.length;i++)
if(this.players[i].name==name)
{
this.players[i] = player;
return;
}
}
this.show = function(){
for(var i=0;i<this.players.length;i++)
console.log(this.players[i].toString());
}
}
var p = new Players();
p.add(new Player('Lorem'));
p.add(new Player('Ipsum'));
p.show();
p.delete('Ipsum');
p.show();

What's unreliable about an array? Use the built in methods IMO. A simple example:
var players = [];
var Player = function(name) {
this.name = name;
this.x = 0;
this.y = 0;
this.toString = function() {
return 'player : ' + this.name + ' at ' + this.x + ', ' + this.y;
};
}
function getPlayerByName(name){
return players.filter(function(p){
return p.name.toLowerCase() === name.toLowerCase();
})[0];
}
// etc...
players.push(new Player('foo'));
var fetched = getPlayerByName('foo');
console.log(fetched);
http://jsfiddle.net/cy39sqge/

Related

for (var o in this) inside an object

I’ve made a little sandbox using the p5.js library : http://gosuness.free.fr/balls/
I’m trying to implement a way to deal with the options on the side, which are toggled using keyboard shortcuts.
This is what I tried to do :
var options =
{
Option: function(name, value, shortcut)
{
this.name = name;
this.shortcut = shortcut;
this.value = value;
this.show = function ()
{
var texte = createElement("span",this.name + " : " + this.shortcut + "<br />");
texte.parent("options");
texte.id(this.name);
}
},
toggle: function(shortcut)
{
for (var o in this)
{
console.log(o);
if (o.shortcut == shortcut)
{
o.value = !o.value;
changeSideText("#gravity",gravity);
addText("Toggled gravity");
}
}
}
};
I instantiate each option inside the object options thus :
var gravity = new options.Option("gravity", false,"G");
var paintBackground = new options.Option("paintBackground",false,"P");
When I call the function options.toggle, console.log(o) gives me "Option" "toggle". but what I want is to get for (var o in this) to give me the list of properties of the object options, which are in this case gravity and paintBackground
How do I do that ?
Thanks !
When You create a instance of Option, its not kept within the variable options, but in the provided variable.
var gravity = new options.Option("gravity", false,"G");
Creates an instance of Option located under gravity variable.
Your iterator for (var o in this) iterates over options properties, with the correct output of the object's methods.
If You want your code to store the new instances of Option within options variable, you can modify code like
var options =
{
instances: [],
Option: function(name, value, shortcut)
{
this.name = name;
this.shortcut = shortcut;
this.value = value;
this.show = function ()
{
var texte = createElement("span",this.name + " : " + this.shortcut + "<br />");
texte.parent("options");
texte.id(this.name);
}
options.instances.push(this);
},
toggle: function(shortcut)
{
for (var i in this.instances)
{
console.log(this.instances[i]);
if (this.instances[i].shortcut == shortcut)
{
this.instances[i].value = !this.instances[i].value;
changeSideText("#gravity",gravity);
addText("Toggled gravity");
}
}
}
};
this is your example working as You intend it to, but i wouldnt consider this as a reliable design pattern.

How can I iterate through this object in js?

I'm trying to go through the object of rooms[room] and save all the names with a property side = 1 to one array and names with side = 2 to a different array.
Such as:
side1['tom','bob'];
side2['billy','joe'];
this is what I have so far, it's getting the sides but how do I get the names as well?
var room = '1';
var rooms = {};
rooms[room] = {};
var player = 'tom'
rooms[room][player] = {};
rooms[room][player]['side'] = 1;
var player = 'billy'
rooms[room][player] = {};
rooms[room][player]['side'] = 2;
var player = 'joe'
rooms[room][player] = {};
rooms[room][player]['side'] = 2;
var player = 'bob'
rooms[room][player] = {};
rooms[room][player]['side'] = 1;
for (var key in rooms[room]) {
if (rooms[room].hasOwnProperty(key)) {
var obj = rooms[room][key];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
console.log(prop + " = " + obj[prop]);
}
}
}
}
In the code you have, the player names are stored in the variable key during your iterations. Try:
// ...
if (obj.hasOwnProperty(prop)) {
console.log(prop + " = " + obj[prop]);
console.log(key); // <- you get the names
}
But IMHO, your code will soon get very messy when you want to have more players. I would suggest having a function that takes care of creating the players and add them to the arrays you want to create during this process. That way you don't have to iterate over the rooms to build the mentioned arrays side1 and side2.
I would suggest something like this:
var rooms = {};
function newPlayer(roomID, player, side) {
if ( !rooms.hasOwnProperty(roomID) ) {
rooms[roomID] = {};
}
rooms[roomID][player] = {};
rooms[roomID][player].side = side;
if ( !rooms[roomID].hasOwnProperty('side' + side) ) {
rooms[roomID]['side' + side] = [];
}
rooms[roomID]['side' + side].push(player);
}
newPlayer(1, 'tom', 1);
newPlayer(1, 'billy', 2);
newPlayer(1, 'joe', 2);
newPlayer(1, 'bob', 1);
console.log(rooms);

Adding onclick event in JavaScript with parameters

I'm trying to make a dropdown to display the results of a request given what the user writes in a field.
The problem I'm encountering is that when I try to add an onclick event to each item in the dropdown, only the last one acts like expected.
The dropdown is a section and I try to include sections in it.
Here is the dropdown :
<section id="projectDrop">
</section>
Here is the code :
var j = 0;
var tmp;
for (var i=0;((i<infos.projects.length) && (i<5));i++)
{
if (infos.projects[i].name.toLowerCase().match(projectName.value.toLowerCase()))
{
projectDrop.innerHTML += '<section id="project' + j + '">' + infos.projects[i].name + '</section>';
tmp = document.getElementById('project' + j);
projectDrop.style.height = (j+1)*20 + 'px';
tmp.style.top = j*20 + 'px';
tmp.style.height = '20 px';
tmp.style.width = '100%';
tmp.style.color = 'rgb(0, 0, 145)';
tmp.style.textAlign = 'center';
tmp.style.cursor = 'pointer';
tmp.style.zIndex = 5;
tmp.onclick = function(name, key)
{
return function()
{
return insertProject(name, key);
};
} (infos.projects[i].name, infos.projects[i].key);
++j;
}
}
The result is visually as I expected, I can see the dropdown with all my projects listed and a pointer while hovering etc...
But only the last project is clickable and trigger the "insertProject" function while the other do nothing.
If someone could help me solve that !
You need to store the key somewhere. Take a look at the solution below, I have used the data-key attribute on the <section> to store the key.
Also note how I have changed the code to create the element object and assign its properties, instead of building a raw string of HTML. The problem with building HTML as a string is you have to worry about escaping quotes, whereas this way you don't.
var j = 0;
var tmp;
for (var i=0;((i<infos.projects.length) && (i<5));i++)
{
if (infos.projects[i].name.toLowerCase().match(projectName.value.toLowerCase()))
{
tmp = document.createElement('section');
tmp.id = "project" + j;
tmp.setAttribute('data-key', infos.projects[i].key);
tmp.innerHTML = infos.projects[i].name;
projectDrop.style.height = (j+1)*20 + 'px';
tmp.style.top = j*20 + 'px';
tmp.style.height = '20 px';
tmp.style.width = '100%';
tmp.style.color = 'rgb(0, 0, 145)';
tmp.style.textAlign = 'center';
tmp.style.cursor = 'pointer';
tmp.style.zIndex = 5;
tmp.onclick = function(){
insertProject(this.innerHTML, this.getAttribute('data-key'));
};
projectDrop.appendChild(tmp);
++j;
}
}
Change:
tmp.onclick = function(name, key)
{
return function()
{
return insertProject(name, key);
};
} (infos.projects[i].name, infos.projects[i].key);
to
tmp.onclick = function(j){
return function(name, key)
{
return function()
{
return insertProject(name, key);
};
} (infos.projects[j].name, infos.projects[j].key);
}(i)

javascript - creating netrual consractor [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Constructors in Javascript objects
im trying to learn how to create class's in javascript. I found that is very diffuclt for me to understand it.
now, i want to know if is possible to create a constractor in javascript, like we can do in c# or other programming languages.
i tried few things:
way 1:
function SiteProfile(_url) {
this.url = "";
this.name = this.ExtractNameFromURL();
}
SiteProfile.prototype.ExtractNameFromURL = function () {
var firstDOT = this.url.indexOf(".");
var secondDOT = this.url.indexOf(".", firstDOT + 1);
var theName = "";
for (var i = firstDOT + 1; i < secondDOT; i++) {
theName += this.url[i];
}
return theName;
}
way 2:
function Site() {
this.url = "";
this.name = "";
this.Site = function (_url) {
this.url = _url;
this.name = this.ExtractNameFromURL();
}
this.ExtractNameFromURL = function () {
var firstDOT = this.url.indexOf(".");
var secondDOT = this.url.indexOf(".", firstDOT + 1);
var theName = "";
for (var i = firstDOT + 1; i < secondDOT; i++) {
theName += this.url[i];
}
return theName;
}
}
both of class's should take a URL, and just get the name from him with out the www. or the .com
i want to know if i can design a class, that i can create an instance like so:
var site = new SiteProfile("www.google.co.il");
document.write(site.name); // becuse, this do nothing
(sorry for my english)
You're real close. The problem with your first form is simply that you are not setting the url property with the _url parameter.
function SiteProfile(_url) {
//change the line below to:
//this.url = _url;
this.url = "";
this.name = this.ExtractNameFromURL();
}
SiteProfile.prototype.ExtractNameFromURL = function() {
var firstDOT = this.url.indexOf(".");
var secondDOT = this.url.indexOf(".", firstDOT + 1);
var theName = "";
for (var i = firstDOT + 1; i < secondDOT; i++) {
theName += this.url[i];
}
return theName;
}
var site = new SiteProfile("www.google.co.il");
document.write(site.name); // with the change above, this will behave as expected
Here's the fiddle for the first form: http://jsfiddle.net/BCnfx/
The problem with the second form is two-fold. The main function should be called "SiteProfile" if you still want to instantiate it as such. The second problem is that you need to initialize the url property by passing in the url to the Site method.
//function below should be called "SiteProfile", not "Site"
function Site() {
this.url = "";
this.name = "";
this.Site = function(_url) {
this.url = _url;
this.name = this.ExtractNameFromURL();
};
this.ExtractNameFromURL = function() {
var firstDOT = this.url.indexOf(".");
var secondDOT = this.url.indexOf(".", firstDOT + 1);
var theName = "";
for (var i = firstDOT + 1; i < secondDOT; i++) {
theName += this.url[i];
}
return theName;
};
}
//now instantiate like this instead.
var site = new SiteProfile();
site.Site("www.google.co.il");
document.write(site.name); // with the changes above, this will behave as expected
Here's the fiddle for the second form: http://jsfiddle.net/BCnfx/1/
in your first example:
function SiteProfile(_url) {
this.url = _url;
this.name = this.ExtractNameFromURL();
}
then you will be able to do :
var site = new SiteProfile("www.google.co.il");
document.write(site.name);

Cannot make unique javascript object. What's wrong with this code?

I'm trying to make a simple in in-page popup called like this:
var test = new popObject({}); //JSON options
and I'm having trouble because when I create two in a row, and call show() on the first one, the second one always shows. Both are created, but they aren't separate somehow, despite being called with new. What am I doing wrong here? I've included my code, but I have removed out the irrelevant functions for compactness.
function popObject(options) {
//functions
show = function() {
console.log(boxselector);
jQuery(boxselector).css("display", "block");
return jQuery(boxselector);
}
var hide = function() {...}
var update = function(updateOptions) {...}
var calcTop = function(passedHeight) {...}
var calcLeft = function(passedWidth) {...}
var calcHeight = function(passedHeight) {...}
var stripUnits = function(measure, auto) {...}
var destroy = function() {...}
//public functions
this.show = show;
this.hide = hide;
this.update = update;
this.destroy = destroy;
//constants
name = options.name; //name should never be changed.
boxselector = ".boxcontainer[name=" + options.name + "]";
boxbodyselector = ".boxbody[name=" + options.name + "]";
boxtitleselector = ".boxcontainer[name=" + options.name + "]"
boxboxselector = ".boxbox[name=" + options.name + "]"
title = options.title;
content = options.content;
width = options.width;
height = options.height;
this.name = name;
this.selectors = [boxselector, boxbodyselector, boxtitleselector, boxboxselector]
this.title = title;
this.content = content;
this.width = width;
this.height = height;
//variables
popupHtml = ...
//init code
jQuery("#dropzone").append(popupHtml); this.init = null;
jQuery(".boxbox[name=" + name + "]").css("top", calcTop(width));
jQuery(".boxbox[name=" + name + "]").css("left", calcLeft(height));
jQuery(".boxbody[name=" + name + "]").css("height", calcHeight(height));
}
This is because you're declaring a lot of variables in the global scope. Try the following code instead:
function popObject(options) {
//functions
this.show = function() {
console.log(boxselector);
jQuery(boxselector).css("display", "block");
return jQuery(boxselector);
}
var hide = function() {...}
var update = function(updateOptions) {...}
var calcTop = function(passedHeight) {...}
var calcLeft = function(passedWidth) {...}
var calcHeight = function(passedHeight) {...}
var stripUnits = function(measure, auto) {...}
var destroy = function() {...}
//public functions
this.show = show;
this.hide = hide;
this.update = update;
this.destroy = destroy;
//constants
var name = options.name; //name should never be changed.
var boxselector = ".boxcontainer[name=" + options.name + "]";
var boxbodyselector = ".boxbody[name=" + options.name + "]";
var boxtitleselector = ".boxcontainer[name=" + options.name + "]"
var boxboxselector = ".boxbox[name=" + options.name + "]"
var title = options.title;
var content = options.content;
var width = options.width;
var height = options.height;
this.name = name;
this.selectors = [boxselector, boxbodyselector, boxtitleselector, boxboxselector]
this.title = title;
this.content = content;
this.width = width;
this.height = height;
//variables
var popupHtml = ...
//init code
jQuery("#dropzone").append(popupHtml); this.init = null;
jQuery(".boxbox[name=" + name + "]").css("top", calcTop(width));
jQuery(".boxbox[name=" + name + "]").css("left", calcLeft(height));
jQuery(".boxbody[name=" + name + "]").css("height", calcHeight(height));
}
Note all the vars that weren't there before. This defines them as local to the function, and thus local to your object (and also, essentially, private... use this. instead of var to make public members).
Anything that isn't declared with a var or a this. is considered global. So, when you called show(), it used the global show, which referenced the object that was created later.
What is boxselector? If it's a generic selector then it would select all elements on the page, regardless if its inside of that unique object.
When you declare something without var or this within a function definition, such as
boxselector = ".boxcontainer[name=" + options.name + "]";
It creates it in the global namespace (attaches it to window)
Try changing this line to
var boxselector = ".boxcontainer[name=" + options.name + "]";

Categories