is there a way to automatically create subobjects in an assignment after construction, i.e.
var obj = {};
obj.a.b.c=13;
the above gives me a "obj.a is undefined" error
i wrote a function to do this, but wondered if there was an easier way
_setObjectProperty(obj,13,['a','b','c']);
function _setObjectProperty(obj,value,loc)
{
if(loc.length>1) {
obj[loc[0]] = obj[loc[0]] || {};
_setObjectProperty(obj[loc[0]],value,loc.splice(1));
}
else if(loc.length===1) {
obj[loc[0]]=value;
}
}
No, there's no built in way to do this in JavaScript. The only way is to create your own function like you did. If you want the convenience of the dot operator/notation you can use the following function:
var set = function(path, value, root) {
var segments = path.split('.'),
cursor = root || window,
segment,
i;
for (i = 0; i < segments.length - 1; ++i) {
segment = segments[i];
cursor = cursor[segment] = cursor[segment] || {};
}
return cursor[segments[i]] = value;
};
set("a.b.c", 2);
console.log(a.b.c) // => 2
Related
I have a C# script like below:
public List<MazePath> BreakIntoConnectedPaths()
{
List<MazeVertex> remainVertices = new List<MazeVertex>(vertices);
List<MazePath> paths = new List<MazePath>();
while (remainVertices.Count > 0)
{
MazePath path = new MazePath();
path.entrancePosition = entrancePosition;
path.exitPosition = exitPosition;
VisitCell(path, remainVertices.First(), null, remainVertices);
paths.Add(path);
//Store the coordinate for entrance and exit
}
return paths;
}
void VisitCell(MazePath path, MazeVertex ver, MazeVertex parent, List<MazeVertex> remainVertices)
{
remainVertices.Remove(ver);
path.Add(ver);
for (int i = 0; i < ver.connectVertices.Count; i++)
{
MazeVertex ver2 = ver.connectVertices[i];
if (ver2 != parent)
{
VisitCell(path, ver2, ver, remainVertices);
}
}
}
I want to convert it to javascript as below
BreakIntoConnectedPaths = function() {
var remainVertices = _.cloneDeep(this.vertices);
var paths = [];
while (remainVertices.length > 0) {
var path = new Path();
path.entrancePos = this.entrancePos;
path.exitPos = this.exitPos;
this.VisitCell(path, remainVertices[0], null, remainVertices);
paths.push(path);
// Store the coordinate for entrance and exit
}
return paths;
}
VisitCell = function(path, vertex, parentVertex, remainVertices) {
_.remove(remainVertices, function(v) {
return v.x === vertex.x && v.z === vertex.z;
});
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
// if (parentVertex && (connectedVertex.x !== parentVertex.x || connectedVertex.z !== parentVertex.z)) {
if(parentVertex && _.isEqual(connectedVertex, parentVertex)) {
VisitCell(path, connectedVertex, vertex, remainVertices);
}
}
}
The _ symbol here is lodash sign.
After I convert to javascript code, the behavior of these functions is difference with the C# one. With the same vertices data, the paths array had returned with difference size.
Thanks you for reading and pls help me if you see my mistake here.
In the C# version, your VisitCell function has a condition that says if(ver2 != parent), but in the JS version you check that they are equal instead of not equal.
Also, that condition would never pass any way because in your first call to that function you pass in null for the parent, but in that condition you check that the parent is "truthy".
Lodash's isEqual can handle null values, so I'm not sure why you're checking if the parent is truthy there. Perhaps you meant to do this?
if(!_.isEqual(connectedVertex, parentVertex)) {
There are several ways to improve your JavaScript code. When transpiling code, it is better to not copy/paste and fix, but to rewrite using the target language instead.
I would prefer to have this written, for example:
var vertices;
var entrancePos;
var exitPos;
function Path(entrancePos, exitPos){
this.entrancePos = entrancePos;
this.exitPos = exitPos;
this.Add = function() {
// your Add() code here
}
}
function breakIntoConnectedPaths() {
var remainingVertices = _.cloneDeep(vertices);
var paths = [];
while (remainVertices.length) {
var path = new Path(entrancePos, exitPos);
visitCell(path, remainingVertices.shift());
// Store the coordinate for entrance and exit
paths.push(path);
}
return paths;
}
function visitCell(path, vertex, parentVertex) {
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
if(_.isEqual(connectedVertex, parentVertex)) {
visitCell(path, connectedVertex, vertex);
}
}
}
Keep in mind that the variables vertices, entrancePos, exitPos and Path are not available to me on your C# code, so I only declare them on JavaScript. Implement them as you may.
Does that fix it, by the way?
I have this closure :
function CFetchNextData(ofs, pag, fetchFunction) {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
}
I then create a variable this way:
var fetchInfo = CFetchNextData(0, 10, specificFetchFunction);
fetchInfo(options, myCB);
So that everytime I call fetchInfo, pagination is automatically set to the next set of data. That works great, althought
I'd like to have multiple instance of : "fetchInfo", each one having its own scope.
var A = fetchInfo; // I'd like a clone with its own scope, not a copy
var B = fetchInfo; // I'd like a clone with its own scope, not a copy
I could do:
var A = new CFetchNextData(ofs, pag, fetchFunction);
var B = new CFetchNextData(ofs, pag, fetchFunction);
But obviously I would have to setup "ofs" and "pag" each time, whereas by cloning fetchInfo, I'd have a stable pagination, set only once and for good.
Do you know how to achieve that ?
Thanks in advance
There isn't a concept of cloning a function in JavaScript. You need to call CFetchNextData (or another function) multiple times if you want to create multiple closures.
You could have CFetchNextData return a factory function instead of returning the actual function. But I'm not sure that's really an improvement.
function CFetchNextDataFactory(ofs, pag, fetchFunction) {
return function() {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
};
}
var fetchInfoFactory = CFetchNextData(0, 10, specificFetchFunction);
var A = fetchInfoFactory();
var B = fetchInfoFactory();
This may not answer all of your question but just to pitch in , you could try assigning your parameters to a default / fallback value which will allow you to avoid setting ofs and pag each declaration . Below is a prototype of what I came up with . Its using oop :
class CFetchNextData {
constructor(ofs, pag){
this.OFS = 1; //default value
this.PAG = 10; //default value
this.ofs = ofs;
this.pag = pag;
if(ofs == null || ofs == undefined){
this.ofs = this.OFS;
}
if(pag = null || pag == undefined){
this.pag = this.PAG;
}
}
fetchInfo(){
var data = this.ofs += this.pag;
return data;
}
}
var task1 = new CFetchNextData(); // Falls back to default values..
var task2 = new CFetchNextData(32,31); // Uses values from specified in args...
document.write(task1.fetchInfo() + "\n")
document.write(task2.fetchInfo())
Hope this helps...
I have a mighty strange JavaScript problem. I have made an object oriented maze generator, which works well, but only if I call "this" (or the alias "self") right before the generator.
See code below:
// Constructor for a maze
function Maze(mazeWidth, mazeHeight) {
// Always working reference to this
var self = this;
// Has the maze been generated?
var generated = false;
// Default dimensions
var width = 20;
var height = 20;
// Check if dimensions are given
if (!isNaN(mazeWidth) && mazeWidth >= 1) {
width = parseInt(mazeWidth);
}
if (!isNaN(mazeHeight) && mazeHeight >= 1) {
height = parseInt(mazeHeight);
}
// The maze itself
var maze = {};
// Populate the maze
for (var y = 0; y < height; y++) {
maze[y] = {};
for (var x = 0; x < width; x++) {
maze[y][x] = new MazeCell(x, y);
}
}
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
}
// For some mighty strange reason "self" (or "this") needs to be called here for the code below to work
self;
// Generate the maze
(function generateMaze() {
// Map directions to its reverse
var directionMap = {};
directionMap[Maze.prototype.N] = Maze.prototype.S;
directionMap[Maze.prototype.E] = Maze.prototype.W;
directionMap[Maze.prototype.S] = Maze.prototype.N;
directionMap[Maze.prototype.W] = Maze.prototype.E;
// Depth-first search to generate the maze
(function DFS(cell, entryDirection) {
// Set the cell as discovered and open the entry direction
cell._setDiscovered();
cell._open(entryDirection);
// Find the neighbour cells
var neighbours = {};
neighbours[Maze.prototype.N] = cell.getNeighbourCell(Maze.prototype.N);
neighbours[Maze.prototype.E] = cell.getNeighbourCell(Maze.prototype.E);
neighbours[Maze.prototype.S] = cell.getNeighbourCell(Maze.prototype.S);
neighbours[Maze.prototype.W] = cell.getNeighbourCell(Maze.prototype.W);
// Check the neighbour cells in random order
for (var i = 0; i < 4; i++) {
var direction = (function() {
var result;
var count = 0;
for (var direction in neighbours) {
if (Math.random() < 1/++count)
result = direction;
}
return result;
})();
var nextCell = neighbours[direction];
delete neighbours[direction];
if (nextCell == false)
continue;
if (nextCell._isDiscovered())
continue;
// Set exit opening of this cell
cell._open(direction);
// Process next cell
DFS(nextCell, directionMap[direction]);
}
})(self.getCell(Math.floor(Math.random()*width), Math.floor(Math.random()*height)), null); // This line is the problem
})();
// ......
If I don't call "self" above the generation code, this.getCell will be called, but the first parameter will be a reference to the generateMaze-function itself. The second parameter will be unset.
It also work if I change the dummy line from "self" to "this".
Just writing "self" (or "this") on an otherwise empty line doesn't really do anything, does it? Why is it needed?
You should add semicolons after assigning a value to an attribute or variable, even if the value is a function, like here:
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
}
It should look like this:
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
};
I think that might be the cause of your problem.
What is the shortest way to create a 2d array of a certain size?
I figured that following code is pretty short, but can you do it with even less code?
var x = 5;
var y = 7;
var my2dArray = (new Array(y)).fill(0).map(
function(){
return new Array(x);
}
);
Array.apply(0, Array(x)).map(function() { return Array.apply(0, Array(y)); })
Simpler if you write a little convenience routine
function make_array(x) { return Array.apply(0, Array(x)); }
then
make_array(x).map(function() { return make_array(y);} )
or if you prefer
make_array(x).map(make_array.bind(0, y))
If you're writing ES6:
Array(...Array(x)).map(() => Array(y));
If you'd prefer to use fill, since you seem to have it available:
Array(x).fill().map(function() { return Array(y); });
or somewhat more concisely in ES6, using an arrow function:
Array(x).fill().map(() => Array(y));
It turns out Array#fill requires no argument; without it, it replaces absent elements with undefineds.
What about building your own Class ?
function Array2D(xSize, ySize, initialValue) {
initialValue=initialValue || 0;
// create an flat empty array filled with the initial value
var length = xSize*ySize;
var innerArray = new Array(length);
for (var i=0; i<length; i++) innerArray[i] = initialValue;
// accessors
this.getAt = function(x, y) { return innerArray[x+xSize*y]};
this.setAt = function(x, y, val) { innerArray[x+xSize*y]=val};
}
Use with :
var myArray2d = new Array2D(5,7);
myArray2d.setAt(1,1,3);
var value = myArray2d.getAt(1,1); // == 3
var anotherValue = myArray2d.getAt(2,2); // == 0
I thought there would already be an answer for this but I can't seem to find one..
How can I run a particular class method on all instances of this class in Javascript?
This has to be done in a situation where I do not know the names of the instances.
I think I could use some sort of static variable inside my class to store all instances, but this doesn't seem to exist in JS
So how to call my method on all existing instances of my class?
Note : just for clarification : I'm not speaking about CSS classes, I'm speaking about objects.
Edit : By Class in Javascript, I mean the creation of a new object on a function:
function something()
{
}
var instance = new something();
You can create a static array and store it on your constructor function:
MyClass.allInstances = [];
MyClass.allInstances.push(this);
However, you need some way to figure out when to remove instances from this array, or you'll leak memory.
In Chrome 62+ you can use queryObjects from the console API - which will not work in native JavaScript code but in the console so it's great for debugging.
class TestClass {};
const x = new TestClass();
const y = new TestClass();
const z = new TestClass();
queryObjects(TestClass)
You'll have to provide a custom implementation.
I would do something like this :
function Class() {
Class.instances.push(this);
};
Class.prototype.destroy = function () {
var i = 0;
while (Class.instances[i] !== this) { i++; }
Class.instances.splice(i, 1);
};
Class.instances = [];
var c = new Class();
Class.instances.length; // 1
c.destroy();
Class.instances.length; // 0
Or like this :
function Class() {};
Class.instances = [];
Class.create = function () {
var inst = new this();
this.instances.push(inst);
return inst;
};
Class.destroy = function (inst) {
var i = 0;
while (Class.instances[i] !== inst) { i++; }
Class.instances.splice(i, 1);
};
var c = Class.create();
Class.instances.length; // 1
Class.destroy(c);
Class.instances.length; // 0
Then you could loop through all instances like so :
Class.each = function (fn) {
var i = 0,
l = this.instances.length;
for (; i < l; i++) {
if (fn(this.instances[i], i) === false) { break; }
}
};
Class.each(function (instance, i) {
// do something with this instance
// return false to break the loop
});
Sorry for such a late reply, but I found myself trying to achieve this and I think this may be a simpler answer.
Say you want all instances of class MyClass, only get instances created at top window level (not including instances created inside a closure):
for (var member in window)
{
if (window[member] instanceof MyClass)
console.info(member + " is instance of MyClass");
}
Keyword 'static' could be used in classes now (but check support), ...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static
class Point{
constructor(x, y){
this.x = x;
this.y = y;
Point.all.push(this);
}
destroy(){
let i = Point.all.indexOf(this);
Point.all.splice(i, 1);
}
static all = [];
}
var p1 = new Point(1, 2);
var p2 = new Point(54, 33);
var p3 = new Point(297, 994);
console.log(JSON.stringify(Point.all)); //[{"x":1,"y":2},{"x":54,"y":33},{"x":297,"y":994}]
p2.destroy();
console.log(JSON.stringify(Point.all)); //[{"x":1,"y":2},{"x":297,"y":994}]
You'll need to store a list of instances yourself:
function someClass(param) {
// add to all
if (this.constructor.all === undefined) {
this.constructor.all = [this];
} else {
this.constructor.all.push(this);
}
// set param
this.logParam = function() { console.log(param); };
}
var instance1 = new someClass(1);
var instance2 = new someClass(2);
for (var i = 0; i < someClass.all.length; i++) {
someClass.all[i].logParam();
}
If memory leaks are a concern then you can create a method for deleting instances when you are done with them:
function someClass(param) {
...
this.destroy = function() {
var all = this.constructor.all;
if (all.indexOf(this) !== -1) {
all.splice(all.indexOf(this), 1);
}
delete this;
}
}