Index a JS object like a Metatable in Lua - javascript

Is there a way to index an object in JavaScript like a Metatable in Lua?
Like for example:
var obj = {a:"a",b:"b",properties:{c:"c",d:"d"}}
metatable(obj,obj.properties) // Make it so that if you try to index something that's not inside the object it will go to the parameter one
console.log(obj.a) // "a"
console.log(obj.c) // "c"
To LMD:
How do I do it for multiple objects? Like for example:
var objs = [
obj1 = {name:"Button";class:"button";properties:{text:"Press this"}]
]
for (i in objs){
metatable(objs[i],objs[i].properties)
}
console.log(objs.obj1.text) // "Press this"

Yes: JavaScript has prototypes. These aren't exactly the same as Lua but can be used for simple metatable indexing purposes. One way to achieve your example would be as follows:
const properties = {c: "c", d: "d"} // prototype
const obj = Object.create(properties) // create object with prototype
obj.a = "a"; obj.b = "b";
console.log(obj.a) // "a"
console.log(obj.c) // "c"
Or if you already have the objects given, as in your second example, you may want to use Object.setPrototypeOf(object, prototype), which is comparable to setmetatable(object, {__index = prototype}) in Lua:
const objs = [{name:"Button", class:"button", properties: {text:"Press this"}}]
for (const obj of objs) Object.setPrototypeOf(obj, obj.properties)
console.log(objs[0].text) // "Press this"
that is, the metatable function you've been searching for literally is Object.setPrototypeOf!

Related

How to make each key of an object as a variable with the same name as the key in Javascript?

I have a javascript object like this:
var obj1={a:1,b:2,c:3}
From this i need to extract each key as a variable like this:
var a=1
var b=2
var c=3
How can this be done?
It can be done using ES6 destructuring, you can explode all the object's properties and assign it in a variable the same as the property's name
var {a,b,c} = obj1;
console.log(a);
console.log(b);
console.log(c);
It's not a great looking solution (this isn't the sort of thing you should be doing - better to work with just the initial object), but you could assign all properties of obj1 to the window object, allowing you to reference them standalone, without having to know the keys beforehand:
var obj1 = {
a: 1,
b: 2,
c: 3
};
Object.assign(window, obj1);
console.log(c);
Try with:
var obj1={a:1,b:2,c:3}
let keys = Object.keys(obj1)
keys.forEach(key => {
let value = obj1[key.toString()];
console.log(key); // Use Key
console.log(value) // Use Value
})

Automated nested objects

A couple of days ago I was having fun with some js, when I came to the question if I could automate object nesting. Of course I'm still a newby so I haven't gotten too far.
But what I got is this:
var a = {};
var stso = ""; storing the second object
function sto(b) { // start the object
a[b] = {};
stso = b;
}
function nmo(...objs) { // nesting more object
console.log(objs[0]);
if(objs.length) { // checking to see that I have at least one variable before proceding
for(i = 0; i < objs.length; i++) { // looping through arguments
a[stso][objs[i]] = {}; // and now I would have to repeat one more more for lever for every argument, meaning, the deeper I want to go into the object, the more nested for loops I have to make.
}
}
}
sto("b");
nmo("c");
a.b.c = "Happy ending!";
console.log(a.b.c); // It seems we still dont have a happy ending
// and as a second example
sto("b");
nmo("c", "d", "e", "f", "g");
a.b.c.d.e.f.g = "Another happy ending!";
console.log(a.b.c.d.e.f.g); // Our second happy ending was also unhappy...
In summary, you define the second object in one function, you define as many objects as you want in your second function in order.
How could I achieve this with my current structure?
If i understand you correctly, you can do something like this:
var createNestedObjects = function( obj ) {
//get all the passed arguments
var args = Array.prototype.slice.call(arguments);
//start with i = 1, as we want to skip obj in the arguments
for( var i = 1; i < args.length; i++ ) {
obj = obj[ args[i] ] = obj[ args[i] ] || {};
}
};
var a = {};
createNestedObjects( a, "b", "c", "d", "e", "f" );
a.b.c.d.e.f = "happy ending";
console.log(a.b.c.d.e.f); //logs "happy ending"
explanation line 3
Your requirement is to pass as many strings as required to the function to create any size of nested objects you would like.
However, as you can see, the function only has one parameter: obj.
The cool thing is that javascript allows you to pass even more parameters and you are still able to access them using the arguments object. The arguments object is available in all functions. The arguments object is similar to an array, but not quite the same as it is an object in it's own, if you log it in this case it will display:
Arguments [{}, "b", "c", "d", "e", "f"] (6)
We can't loop through the agruments object using a for loop, so on line 3 it's converted to an array first.
arguments object reference
explanation inside the loop
There are two interesting parts in this line. Javascript allows you to assign multiple variables at once as the assignment operator assigns a value to its left operand based on the value of its right operand
var a, b;
a = b = 10; //both a and b are set to 10
Using || operator is used here to set a default value ( in this case {} )if the value on the left is undefined. Which is for example also handy to set default values
function setDelay ( delay ){
delay = delay || 60
return delay
}
setDelay( ) //no parameter is passed, delay is set to 60
setDelay( 120 ) 120 is passed and delay is set to 120
In this case the line
obj = obj[ args[i] ] = obj[ args[i] ] || {};
can be rewritten as:
var name = args[i]; // first loop args[i] is "b"
if(!obj[name]){ // a.b does not exist
obj[name] = {}; // a.b is set to {}
}
obj = obj[name]; // a is set to a.b
which checks if there is already an object with that name, if not it's created and set as obj so we can nest objects in the next loop.
I hope this clarifies the code
So, you want to convert values to a nested object? This can be done by doing something like this:
let values = ['b', 'c', 'd', 'e', 'f', 'g'];
let a = {};
// Every time we loop, we change the root
let root = a;
for (let value of values) {
// Set root to the value of the array and assign a new object
root = root[value] = {};
}
// Set the string
a.b.c.d.e.f.g = "Happy ending!";
console.log(a.b.c.d.e.f.g);
console.log(a);
It basically does this inside the loop:
a = a[b] = {};
a[b] = a[b][c] = {};
a[b][c] = a[b][c][d] = {};
a[b][c][d] = a[b][c][d][e] = {};
a[b][c][d][e] = a[b][c][d][e][f] = {};
a[b][c][d][e][f] = a[b][c][d][e][f][g] = {};
It creates a new key every loop and assigns an empty object to it. The next iteration creates a new key (with an empty object as value) inside the newly created object from the previous loop. This goes on until all values of the array have been assigned.

Using Javascript object as object key

I am trying to devise a way to use a simple Javascript object (one level deep key value pairs) as a key to another object. I am aware that merely using the object without stringification will result in [Object object] being used as the key; see the following: Using an object as a property key in JavaScript (so this question is not a duplicate).
There is a blog post about it that takes this into account and also accounts for the need to sort by object key since their order is not guaranteed, but the included Javascript code runs over a 100 lines. We are using the underscore.js library since that goes hand in hand with backbone but pure Javascript alternatives will also be of interest.
In ECMAScript 6 you'll be able to use Maps.
var map = new Map();
var keyObj = { a: "b" },
keyFunc = function(){},
keyString = "foobar";
// setting the values
map.set(keyObj, "value associated with keyObj");
map.set(keyFunc, "value associated with keyFunc");
map.set(keyString, "value associated with 'foobar'");
console.log(map.size); // 3
// getting the values
console.log(map.get(keyObj)); // "value associated with keyObj"
console.log(map.get(keyFunc)); // "value associated with keyFunc"
console.log(map.get(keyString)); // "value associated with 'a string'"
console.log(map.get({ a: "b" })); // undefined, because keyObj !== { a: "b" }
console.log(map.get(function(){})); // undefined, because keyFunc !== function(){}
console.log(map.get("foobar")); // "value associated with 'foobar'"
// because keyString === 'foobar'
I wrote a hash table implementation that accepts arbitrary keys but I suspect you'll reject it on the grounds of the relatively large file size.
https://code.google.com/p/jshashtable/
Here is an underscore based solution that relies on first converting the object to key-value pairs.
var myObj = { name: 'john', state: 'ny', age: 12};
var objPairs = _.pairs(myObj);
var sortedPairs = _.reduce(_.keys(myObj).sort(), function(sortedPairs, key) {
var pair = _.find(objPairs, function(kvPair) {return kvPair[0] == key});
sortedPairs.push(pair);
return sortedPairs;
}, []);
console.log(JSON.stringify(sortedPairs)); //stringifying makes suitable as object key
// [["age",12],["name","john"],["state","ny"]]
You could use a pattern like this. This way, your key for an object is this random id that you generate for every object.
var MyObject = function(name) {
this.name = name;
this.id = Math.random().toString(36).slice(2);
}
MyObject.prototype.toString = function() {
return this.id;
}

C structure to JSON

I am very new to JS, have been working in C/C++ before,
I need an equivalent of below C structure in JSON
struct tmp_t{
int a;
char c_str[1024];
};
struct tmp2_t{
int a2;
.
.
char c2_str[1024];
};
struct my {
int number;
struct tmp_t tmp[100];
struct tmp2_t tmp2[100][1000];
};
For a json like
var myJSON = {
"number":0,
.
.
};
I need to access it like
myJSON.tmp[0].a = 10;
myJSON.tmp2[0][1].c2_str = "hello world"
any input is highly appreciated
Javascript properties are not typed like they are in C so there is no purely "equivalent" expression in javascript. You don't predeclare typed data structures like your C code has. I given variable or property in javascript can be assigned any value or reference - there is not hard typing. So without variables that can only contain a specific type like C has, there's no pre-declaring of data structure definitions like you have included from C.
Instead, you just declare the properties you want to use on a live object or if you intend to use many of them, you can create a prototype which you can instantiate when needed.
A direct declaration of a live object instance somewhat like your last structure would look like this:
var my = {
number: 10,
tmp: new Array(100),
tmp2: new Array(100)
};
This would declare an object named my that had three properties called number, tmp and tmp2. number initially contained the number 10 and the other two properties contained arrays of length 100 who's values were undefined. I don't know of any compact way to predefine your two dimensional array in javascript without running code in a loop to initialize it.
This data defintion would let you access my.number, my.tmp and so on.
If you want your arrays to contains objects with properties themselves, then you need to populate those arrays with the objects.
var my = {
number: 10,
tmp: [{a: 1, c_str: "foo"}, {a: 2, c_str: "whatever"}],
tmp2: new Array(100)
};
Or, in code, you could add in item to the tmp array with code like this:
var my = {
number: 10,
tmp: [],
tmp2: []
};
my.tmp.push({a: 1, c_str: "foo"});
my.tmp.push({a: 2, c_str: "whatever"});
Or, you could create the object separately and then put it in the array:
var obj = {}; // new empty object
obj.a = 1; // assign property
obj.c_str = "foo"; // assign property
my.tmp.push(obj); // put object into the array
obj = {}; // new empty bject
obj.a = 2;
obj.c_str = "whatever";
my.tmp.push(obj);
Or, you could assign each property individually like this:
my.tmp.push({}); // put empty object into the array
my.tmp[0].a = 1; // assign property to the object
my.tmp[0].c_str = "foo"; // assign property to the object
my.tmp.push({});
my.tmp[1].a = 2;
my.tmp[1].c_str = "whatever";
In either case, you could then access the data like this:
console.log(my.tmp[0].a); // 1
console.log(my.tmp[0].c_str); // "foo"

Does Javascript have associative arrays?

Does Javascript have associative arrays? Please explain.
Nope; JavaScript arrays are just numeric keys and mixed values. The same thing can be achieved (or, actually, it's exactly the same as associative arrays in other languages) with objects:
var foo = {
a: 123,
b: 'CDE'
};
foo.a; // 123
foo['a']; // 123
You could use arrays:
var foo = [];
foo.a = 123;
foo.b = 'CDE';
foo.b; // CDE
foo['b']; // CDE
HOWEVER, this should never be done because this will not enter the key/value pairs into the array, but add them to the array object as properties. (besides, {a: 123} is easier than a = []; a.a = 123) If you need key/value pairs, use Objects. If you need an enumerated list, use arrays.
This answer is pretty much a copy-paste of my previous answer on this question.
The situation has changed in the five years since this question was asked.
Due to weak typing associative arrays can be faked in JavaScript:
>> var names = new Array();
undefined
>> names["first"] = "Dotan";
"Dotan"
>> names["last"] = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
That is sometimes useful, and all browsers released since 2012 support it, but there are caveats! The array cannot be simply read back:
>> names
Array [ ]
More importantly, the array's length cannot be easily retrieved:
>> names.length
0
Therefore this is not an associative array in the sense that JavaScript would have supported it had it been intended, but rather a workaround that is often useful if for whatever reason a real JS object does not support what you need:
>> var names = {};
undefined
>> names.first = "Dotan";
"Dotan"
>> names.last = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
>> names
Object { first: "Dotan", last: "Cohen" }
>> Object.keys(names).length
2
The closest we have is an object; the easiest way you can define this is using object literal syntax.
var assocArray = {
key: 1,
key2: 2
};
You should be wary of a few things however:
It does not have a .length property.
You should use for in to iterative over it, rather than for(;;;);, but should combine it with hasOwnProperty():
for (var x in assocArray) {
if (assocArray.hasOwnProperty(x)) {
// x = the key, assocArray[x] = the value
}
}
There is no concept of ordering/ sorting the members. Whilst all implementations I know of iterate the members in the order they were added, this is not standardised.
Instead of associative arrays. Javascript has objects. Properties of an object are addressed using a string.
var obj1 = {}; // declare empty object
var obj2 = {a: 1, b: 'string', c: [4,5]}; // obj with 3 properties, a, b, and c
// note that the 'c' property contains an anonymous array
alert(obj2.a); // shows 1
obj2.a = 'another string'; // redefine the 'a' property
obj2.cookie = 'oatmeal'; // add a new property to the object
obj2['ice_cream'] = {vendor: 'Beyers',
flavor: 'Chocolate Surprise'}; // add anonymous object as
// a new property for the object
assert(obj2.a === obj2['a']); // two ways to retrieve the value
var i = 'a'; // using an index varable
assert(obj2.a === obj2[i]); // note the i does not have apostrophes around it
See the Quirksmode docs
Something comparable in JavaScript is an object.
var my_obj = { key : 'value' }
Sure it does (kind of, use objects)
var foo = {
bar: "hello"
}
accessible with
foo.bar

Categories

Resources