Optimized the color render function - javascript

I have a big data to handle.
They need to be classified into 4 colors, and render to the SVG.
My function is:(parameter A B C are used to do something....)
function mapRender(dataArray,A,B,color1,color2,color3,color4,C){
//......do somthing.......
var colorPlusAry = $.grep(dataArray, function(item,key){
var vote_percentage = parseInt(item.vote_percentage);
var result = vote_percentage>=0 && vote_percentage <50;
return result;
});
//......do somthing.......
}
I use grep to generate the new array which the item has same color, and render to SVG.
function colorDistrict(colorArray,color){
var village = '';
var fillColor = 'fill:'+color;
for(var item in colorArray) {
village = colorArray[item].village;
$('svg').find('path').each(function(){
var id = $(this).attr('id');
if(village) {
if(id.substring(6) === village){
$(this).attr('style',fillColor);
}
}
});
}
}
colorDistrict(colorPlusAry,color1); //Render 4 array
It works successfully, but the data is too large and make render slowly, when I trigger the function, it takes several seconds to react...
How can I optimize this function to render the color?

Optimization is a difficult business without the real data, and without knowing the precise DOM structure. I can only give some hints from what I see:
The costly process is interacting with the DOM. Looking at the colorDistrict() function, the two loops seem to be independent. It would then make sense to run the .each() loop only once, and the loop over the colorArray as the nested one. The latter only contains precomputed values and should run much faster.
Looking at what these loops really do, you can write them much more semantic. You compare the collection of all paths and the colorArray for intersection, and then assign a style to a filtered list of paths.
function colorDistrict (colorArray, color){
var fillColor = 'fill:' + color;
// filter the paths to find those mentioned in colorArray
var relevantPaths = $('svg').find('path').filter(function () {
var village = $(this).attr('id').substring(6);
// boolean to indicate the village is part of the colorArray
var included = colorArray.some(function (item) {
return item.village === village;
});
return included;
});
relevantPaths.attr('style', fillColor);
}
If I understand correctly what you are doing, the colorDistrict() function is executed multiple times, once for every color you assign. Do the <path> elements change between rendering the different colors? If not, you should execute $('svg').find('path') only once and cache the found paths for reuse inside that function.
var paths = $('svg').find('path');
function colorDistrict (colorArray, color){
var fillColor = 'fill:' + color;
var relevantPaths = paths.filter(function () {
...
});
}

Related

Full control of many objects Javascript

I am working on a game in javascript that spawns and terminates shapes. Therefore, I need full control over these shapes that are being spawned. I want to do the following, but I am not sure on how to do it:
I want to define a constructor Shape.
Then, I want to make a function so that when it is called, it applies to ALL of the Shape in existence and being able to change each ones attribute. (For example, I call function moveAll() and all of the shapes' this.y increments by one.)
I also want to be able to terminate a specific Shape with a function, or add a Shape with a function.
My main problem is this:
var x = new Shape();
var y = new Shape();
...
I don't want to make a million variables. I need a way to make tons of shapes at once and still be able to control each one individually. And if I made an array, how would I control everything individually?
Anything helps, I just need a basis to understand the concept of constructors. Thanks in advance!
You could make an array of shapes:
let shapes = []
// this will create 6 shapes
for(let i of [0,1,2,3,4,5]) {
shapes.push(new Shape())
}
If a shape has an id you can terminate it:
// incrementing id counter
let id = 0
// x and y are optional starting positions
function Shape(x, y) {
this.id = id++
this.y = y || 0
this.x = x || 0
}
// remove the shape from the array
function terminate(id) {
shapes = shapes.filter(shape => shape.id !== id)
}
// or add a shape
function spawn() {
shapes.push(new Shape())
}
Now you have your shapes.
The simplest way to move all shapes would be something like this:
function moveAll() {
shapes.forEach(shape => shape.y++)
}
Runtime for this will increase as the number of shapes increase.
updated moveAll, per request
function moveAll() {
shapes.forEach(shape => {
if(shape.type === true) {
shape.y++
} else {
shape.y--
}
})
}

How to partition linked list around minimum value

I am trying to perform a selection sort on a linked list using recursion and I am having some trouble partitioning my linked list around the node with the smallest value on each pass through my recursive sort function. I am trying to get the node with the smallest value, partition the linked list around the smallest value, append the smallest to the front, join the two partitioned lists, and then perform the sort again on the joined partitioned list until the entire linked list is sorted. For example:
q w e r t // partition around e
e -> q w r t // join the partitions
eq -> w r t // append q to e
eq -> w r t // partition around r
and so forth.
My sort method:
Node.prototype.sort = function(){
if(!next){
return this;
} else {
var a = null;
var b = null;
var smallest = this.smallest();
splitIt(smallest, a, b);
appendSmallest(smallest);
a.join(b);
a.sort();
}
}
I get the smallest node like so:
Node.prototype.smallest = function(){
if(!next) return this;
var sm = next.smallest();
if(sm.data < this.data){
return sm;
}
return this;
}
Here are my append and join methods:
Node.prototype.appendSmallest = function(smallest){
if(!next) next = smallest;
}
Node.prototype.join = function(otherNode){
if(!next) next = otherNode;
else next.join(otherNode);
}
I am having some trouble implementing the splitIt method recursively. What would the pseudocode be for such operation?
I'm assuming you are using pure JavaScript, as there is no indication otherwise.
In your code you use several times the word Node as kind of a variable type in a way that is not valid JS. You declare variables with the word var (and in ECMAScript6 let for block scoped variables). Look at this question. So for example in smallest you write:
var sm = next.smallest();
In sort you have two additional problems: first, you pass null variables as parameters in hope that the function will assign objects that will replace them (see the explanation here regarding the nature of reference valued variables (not primitive valued) in JS). Second, assuming you forgot but meant to have this line in appendSmallest
else { next.appendSmallest(smallest);}
then I think you have an infinite loop, as smallest is appended to this linked list, which is (if splitIt works properly) is the same as a.
My suggestion is doing the split and join together as a "spitSmallest" function:
Node.prototype.spitSmallest = function(smallest){
//we know that this node is not smallest
if (this.next == smallest){
this.next = this.next.next;
smallest.next = null; //again not really needed
} else {
this.next.spitSmallest(smallest);
}
}
Node.prototype.sort = function(){
if(!this.next){
return this;
} else {
var smallest = this.smallest();
var restHead;
if (smallest==this){
restHead = this.next;
this.next = null; //not needed but makes it more readable
} else {
restHead = this;
this.spitSmallest(smallest);
}
smallest.next = restHead.sort();
return smallest;
}
}

Is it possible to store both a number and name for a value in an array?

I'm currently writing a function to pre-load all images used by a small game to draw on an array. Currently I store the paths to the sources in two different array to solve this, but would it be possible to have an array that can use both a number i or a name n when getting a value from it? This would help it be easier to use the value to assign as a search on my pictures later on, and using gameimage[153] as a source value doesn't look very tidy and I'd rather use gameimage["snakehead"]
current code example:
//add images to the gameimages array to be loaded before gamestart
//add the snake images
var gameimages = [];
gameimages.push("snake30px.png", "snake30pxdown.png", "snake30pxup.png","snake30pxauch.png");
var gameimagesnumber = gameimages.length;
//start the startGame() when all images in the gameimages array is loaded, to avoid albino snake and lack of stuff to crash into
//NOTE: This is kinda "hackish" as the images is just loaded to make sure it is cached in browser...
//they're not actually used, but seem to have the same effect :x
for(var i = 0; i < gameimagesnumber; i++){
console.log("Loading " + gameimages[i]);
var image = new Image();
image.onload = function(){
//add the image in gameimagesnames for easier use in the code when needed
gameimagesnames[this.src.substring(this.src.lastIndexOf("/") + 1,this.src.length - 4)] = this.src;
checkforgamestart();
};
image.src = "images/" + gameimages[i];
}
//checking for gamestart
function checkforgamestart(){
if(gameimagesnumber > 1) gameimagesnumber--;
else startGame();
}
Absolutely!
In JS, you can make an array of any data type. You also have access to objects. So let's combine those.
var obj = {
name: 'a test name',
value: 1
}
var array = [obj];
array[0].name; // == 'a test name'
array[0].value; // == 1
var anotherObj = {
name: 'another name',
value: 7
}
array.push(anotherObj);
array[1].name; // == 'another name'
array[1].value; // == 7
Reading your question in more detail, I see you're also looking to have a get method that can pull from either value. That's a bit trickier.
The other answer provided will do this, but stores the data in two separate locations in the object (not an array), and also loses the array prototypes.
To better solve this within the Array class type, let's just take advantage of Array.filter!
array.filter(function(item) { return item.name === 'another name' })
This will provide you with a subArray of elements that meet whatever criteria you provide within the given callback function. In this case, using my array above, it would pass back an array with one element in it; anotherObj.
If you want to access by both, use object
var arr = {}
arr[1] = arr['myKey'] = 'myValue'
Then you can access them both by number and by key.

How to avoid messy nested if-else or switch

Function AM()
{
var source = new Array("External","Chassis","Internal");
var shape = new Array("Sine","Square", "Ramp","Nramp","Triangle","ARB");
var depth = new Array ("100","0","50");
var frequency = new Array("100","0.002","20000","1000");
var currentSource;
var currentShape;
var currentDepth;
var currentFrequency;
for (var item in source)
{
currentSource = source[item];
FG_AMSource().Keys(source[item] );
for (var item in shape)
{
currentShape = shape[item];
FG_AMShape().Keys(shape[item] );
for (var item in depth)
{
currentDepth = depth[item];
FG_AMDepth().Keys(depth[item]);
for (var item in frequency)
{
currentFrequency = item;
FG_AM_Frequency().Keys(frequency[item]);
CheckImage2(currentSource, currentShape,currentDepth, currentFrequency);
}// shape
}// depth
}// shape
}//source
}
FG_AMSource() is a function that allows me to set the setting. With what I have here, I am able to loop through all combinations of Source, Shape, Depth and Frequency.
My problem here lies in the CheckImage2 function. With each combination I get, I must call this CheckImage2 function, pass in the current Source, Shape, Depth and Frequency, then do checking accordingly. Therefore, inside my CheckImage2 function, it will look something like
Function CheckImage2(currentSource, currentShape, currentDepth, currentFrequency)
{
switch (currentSource)
case "External":
switch (currentShape)
case "Sine":
switch (currentDepth)
case "100":
switch (currentFrequency)
case "100": //External+Sine+100+100
case "0.002": //External+Sine+100+0.002
//etc etc etc you get the idea
//and I need to include all possible combinations
}
What should I do instead?
There are couple of ways to get around with it:
1) If the action can be formulated, then you should create the formula rather than creating nested for loop or switch. Something like
Function CheckImage2(currentSource, currentShape, currentDepth, currentFrequency)
{
//example, suppose it can be formulated as a string
string formulatedString = currentSource + currentShape + currentDepth + currentFrequency;
//do something else, because things can be formulated instead of listed
}
This is because what you actually need is a handler/function which takes any of the combination.
2) If the number of combination is not that many to populate, try using HashMap or Map or whatever equivalent and pre-populate the possible combinations such that when you use you simply need to call hashMap[key] and at most 1 for loop then act accordingly instead of having nested loops
3) Whenever possible, you can also break them into pieces of smaller independent functions which depend only on one (or at least fewer) element at a time (rather than all of them).
4) Consider of creating a proper class and using Tree structure to work around with it

How to represent entities in a grid

I'm attempting to simulate a bunch of simple entities moving around on a grid.
Individual entities will have specific properties and the grid will be a two dimensional array of square cells that may at any time contain anywhere from zero to multiple entities.
What type of data structures would be appropriate here given that I don't want to over engineer things but at the same time see problems with the obvious simple solutions?
If I have an array of entities where each entity has it's coordinates, then this is fine for populating the grid each step. But this makes it so if I want to know something about a given location, then I have to loop through the entire entity array to see if there's an entity at that location. (or adjacent to it if I needed to know something like that)
And the opposite (having the grid array keeping track of everything) runs into the reverse problem where if I want to know something specific about an entity (like it's name or fitness) I need to search through the whole grid array to find the entity I'm looking for.
And finally there's my third approach which seems over engineered: Have an array of all the entities and the grid array be a bunch of arrays of pointers to whatever entities are in that cell. In this third one I think the cells would need to know which entities were in them and the entities would need to know which cell they were in. And I'm not even sure if I want entities to know their coordinates. It might make more sense for them to only recognize their surroundings and then move, relative to their own location. Also, I'm doing this in javascript, so pointers are probably only logically possible with some hack and I'm trying to not over engineer things.
Are these objects, grid and entity, in a system where they each only have relevance when connected to the other? What I mean is will your entity objects only ever be used in this grid? And will this grid object only ever hold entity objects? If so, then you can link them together not just from the grid's perspective, but also from the entity.
I assume that your grid is made up of a main grid object, which likely consists of a collection of rows, and each row has a collection of cells. Each cell of course being equivalent one [x][y] location in an array. Each of these cells will have a collection of entity objects.
If you create your objects to be "hierarchically referential" it will be easy to move through the list. The grid may have a collection of rows, but each row should have a parent property that references to the grid it's apart of. The rows may have a collection of cells, but each cell should have a parent property that refers to its parent row. And each entity object should have a cell property that refers to the cell of which it is apart.
When you create your move() function for the entities, it will need to 1) remove itself from its current cell entity collection, 2) add itself to the new cell collection, and 3) update the entity.cell property to refer to the new cell.
This is an untested mock-up of the kind of structures I'm talking about:
function grid(height, width) {
this.rows = [];
this.cellAt = function(row, cell) {
return this.rows[row].cells[cell];
};
this.entitiesAt = function(row, cell) {
return this.cellAt(row, cell).entities;
};
this.addRow = function() {
var r = new row(this);
this.rows.push(r);
return r;
};
//create the grid in the default size.
if (height != null && width != null) {
var i, j, r;
for (i = 0; i <= height; i++) {
r = this.addRow();
for (j = 0; j <= width; j++)
r.addCell();
}
}
}
function row(parentGrid) {
this.grid = parentGrid;
this.cells = [];
this.getIndex = function() {
return this.grid.rows.indexOf(this);
};
this.addCell = function() {
var c = new cell(this);
this.cells.push(c);
return c;
};
}
function cell(parentRow) {
this.row = parentRow;
this.entities = [];
this.getIndex = function() {
return this.row.cells.indexOf(this);
};
this.addEntity = function(entity) {
this.entities.push(entity);
entity.cell = this;
};
this.removeEntity = function(entity) {
var i = this.entities.indexOf(entity);
if (i >= 0) this.entities.splice(i, 1);
entity.cell = null;
};
this.removeEntityAt = function(index) {
if (this.entities.length < index && index >= 0) {
e = this.entities[index];
this.entities.splice(index, 1);
e.cell = null;
}
};
}
function entity() {
this.cell = null;
this.getLocation = function() {
return {
"row" : this.cell.row.getIndex(),
"cell" : this.cell.getIndex()
};
};
this.move = function(row, cell) {
var g = this.cell.row.grid;
this.cell.removeEntity(this);
g.cellAt(row, cell).addEntity(this);
};
}
Note: Careful with indexOf(). It's helpful, but not fully cross browser.
Actual usage would look something like this.
Create the new grid
var grid1 = new grid(100, 100);
Create a new entity and add it to the grid:
var e = new entity();
grid1.cellAt(12, 23).addEntity(e);
Move an entity:
e.move(53, 23);
There is potentially a lot more here. For instance, if this grid, row, cell, and entity objects are going to represent actual HTML elements, in the creation script you can have it create those HTML elements. For example, you might want to create a div element when a new row and/or cell is created. One way to link the array to the div so that you have a connection between the logical grid and the "physical" element:
<div data-row="12" data-cell="23"><div>
Bottom line is that I think creating these structured and "hierarchically referential" objects will make it easier for you to move around in the gird. Also, it would be super easy to modify this to support multiple grids in one document.

Categories