I'm trying to select a random set of three unique elements from an array. I'm newish to JS, and I'm constantly tripping over reference behavior that is unexpected (Python is my best language). I think that's happening here, too. This is P5.JS.
Here's my attempt:
var points = [[0,0],[.5*w,0],[w,0],
[0,.5*h],[.5*w,.5*h],[w,.5*h],
[0,h],[.5*w,h],[w,h]];
var vert = [];
var start = int(random(0,8));
vert.push(points[start].slice());
points.splice(start,1);
var middle = int(random(0,7));
vert.push(points[middle].slice());
points.splice(middle,1);
var end = int(random(0,6));
vert.push(points[end].slice());
When I look at the contents of vert, it's clear that I'm not getting the elements that I expected. In particular, I'm never getting any of the last three elements in the original array.
As noted above, the int() and random() are p5.js functions, and fine. The issue was fixed by removing the .slice() instances in the push() statements:
var vert = [];
var start = int(random(0,8));
vert.push(points[start]);
points.splice(start,1);
var middle = int(random(0,7));
vert.push(points[middle]);
points.splice(middle,1);
var end = int(random(0,6));
vert.push(points[end]);
Related
In order to make maps, I need to import some values from csv to json directly in the code.
For loading json and csv files, I use an asynchronous operation with Promise object and I use two loops and a common key to add new properties on json file.
for (var i=0; i< fr[1].length;i++){
var csvId = fr[1][i].codgeo;
var csvValue1 = parseFloat(fr[1][i].value1);
var csvValue0 = parseFloat(fr[1][i].value0);
for (var j=0; j<topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features.length;j++){
var jsonId = topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features[j].properties.codgeo;
if (csvId === jsonId) {
topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features[j].properties.value1 = csvValue1;
topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features[j].properties.value0 = csvValue0;
break;
Everything is working but show up the map on the web takes time.
Is there a way to optimize the loading time of the map ?
Here is a sample of my code : https://plnkr.co/edit/ccwIQzlefAbd53qnjCX9?p=preview
I took your plunkr and added some timing points to it, ran it a bunch of times and got some data on where your script takes its time:
Here's a block with the logging.
I am pretty sure my bandwidth where I live is below average and has a ton of variability; the file load time showed a lot of variability for me, down to 500 milliseconds and up to 1800 milliseconds, everything else was consistent
Let's take a closer look a the data manipulation stage, which you include in your question:
for (var i=0; i< fr[1].length;i++){
var csvId = fr[1][i].codgeo;
var csvValue1 = parseFloat(fr[1][i].value1);
var csvValue0 = parseFloat(fr[1][i].value0);
for (var j=0; j<topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features.length;j++){
var jsonId = topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features[j].properties.codgeo;
if (csvId === jsonId) {
topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features[j].properties.value1 = csvValue1;
topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8).features[j].properties.value0 = csvValue0;
break;
The nested for statement runs approximately 5,151 times by my count. The parent for statement runs 101. These shouldn't change as your data is fixed. Why do these cycles take so long? Because you are calling topojson.feature() every for iteration:
If I isolate this line:
topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8)
We can see that this actually takes a few milliseconds alone.
Topojson.feature
Returns the GeoJSON Feature or FeatureCollection for the specified
object in the given topology. If the specified object is a
GeometryCollection, a FeatureCollection is returned, and each geometry
in the collection is mapped to a Feature. Otherwise, a Feature is
returned. The returned feature is a shallow copy of the source object:
they may share identifiers, bounding boxes, properties and
coordinates. (from the docs).
So, everytime we use topojson.feature we are essentially converting the topojson to geojson. We don't need to do this in the for loop. Let's do that once:
var featureCollection = topojson.feature(fr[0],fr[0].objects.dep_GEN_WGS84_UTF8);
//Merge csv & json
//Add properties from csv to json)
for (var i=0; i< fr[1].length;i++){
var csvId = fr[1][i].codgeo;
var csvValue1 = parseFloat(fr[1][i].value1);
var csvValue0 = parseFloat(fr[1][i].value0);
for (var j=0; j<featureCollection.features.length;j++){
var jsonId = featureCollection.features[j].properties.codgeo;
if (csvId === jsonId) {
featureCollection.features[j].properties.value1 = csvValue1;
featureCollection.features[j].properties.value0 = csvValue0;
break;
}
}
}
Of course, we have to update the portion of code that renders to use the featureCollection variable too, rather than the topojson
Let's take a look at timing now:
Here's an updated bl.ock based on the one above, also with timing points.
No, I didn't forget to include a time for manipulation, it just averaged 1.5 milliseconds for me. Yes, the variability in my bandwidth shows - but the time spent on other manipulation should be clearly less regardless of external factors
Further Enhancements
Preprojection of geometry, see this question/answer.
Simplification of geometry, see mapshaper.org (though I believe you have already done this).
Removal of non-necessary attributes from csv or topojson - are you really using the population field in the topojson, do you need both libgeo and libgeo_m in the topojson (eg: "libgeo":"Puy-de-Dôme","libgeo_m":"PUY-DE-DÔME")?
I have several JavaScript arrays, each containing a list of pointers to objects. When an object meets a certain condition, its pointer must be removed from its current containing array and placed into a different array.
My current (naive) solution is to splice out the exiting array elements and concatenate them onto the array they are entering. This is a slow method and seems to fragment memory over time.
Can anyone offer advice (general or JS-specific) on a better way to do this?
Demonstration code:
// Definitions
TestObject = function() {
this.shouldSwitch = function() {
return(Math.random() > 0.9);
}
}
A = [];
B = [];
while(A.length < 500) {
A.push(new TestObject());
}
// Transfer loop
doTransfers = function() {
var A_pending = [];
var B_pending = [];
for(var i = 0; i < A.length; i++) {
if(A[i].shouldSwitch()) {
B_pending.push(A[i]);
A.splice(i,1);
i--;
}
}
for(var i = 0; i < B.length; i++) {
if(B[i].shouldSwitch()) {
A_pending.push(B[i]);
B.splice(i,1);
i--;
}
}
A = A.concat(A_pending);
B = B.concat(B_pending);
}
setInterval(doTransfers,10);
Thanks!
For a language-independent kind of solution to this problem, when you're transferring elements from one contiguous sequence (array) to another, it's not appending elements to the back of the new array that's going to be bottlenecky (constant time complexity), it's going to be the removal of elements from the middle of your existing container (linear time complexity).
So the biggest benefit you can get is to replace that linear-time operation of removing from the middle of the array with a constant-time operation that still uses that cache-friendly, contiguous array representation.
One of the easiest ways to do this is to simply create two new arrays instead of one: a new array to append the elements you want to keep and a new array to append the elements you want to transfer. When you're done, you can swap out the new array of elements you want to keep (not transfer) with the old array you had.
In such a case, we're exchanging linear-time removals from the middle of a container with amortized constant-time insertions to the back of a new one. While insertion to the end of a container still has a worst-case complexity of O(N) for reallocations, it occurs infrequently enough and is still generally far better than paying for an operation that has an average complexity of O(N) every time you transfer a single element by constantly removing from the middle.
Another way to solve this problem that can be even more efficient, especially for certain cases like really small arrays since it only creates 1 new array, is this:
... when you transfer an element, first append a copy of it (possibly just a shallow copy) to the new container. Then overwrite the element at that index in the old container with the element from the back of the old container. Now simply pop off the element at the back of the old container. So we have one push, one assignment, and one pop.
In this case, we're exchanging a linear-time removal from the middle of a container with a single assignment (store/move instruction) and a constant-time pop from the back of the container (often basic arithmetic). This can work extremely well if the order of the elements in the old array can be shuffled around a little bit, and is often an overlooked solution for getting that linear-time removal from the middle of the array into one with constant-time complexity from the back of the array.
splice is pretty harmful for performance in a loop. But you don't seem to need mutations on the input arrays anyway - you are creating new ones and overwrite the previous values.
Just do
function doTransfers() {
var A_pending = [];
var B2_pending = [];
for (var i = 0; i < A.length; i++) {
if (A[i].shouldSwitch())
B_pending.push(A[i]);
else
A_pending.push(A[i]);
}
var B1_pending = [];
for (var i = 0; i < B.length; i++) {
if (B[i].shouldSwitch())
A_pending.push(B[i]);
else
B1_pending.push(B[i]);
}
A = A_pending;
B = B1_pending.concat(B2_pending);
}
I want to represent a board game in javascript and for that i need a bidimensional array.
I have different board sizes, so i need to initialize the array with the board size in the beginning. I am a java programmer so i know that in java when you want a bidimensional array you just do:
int board_size = 6;
String board = new String[board_size][board_size];
How can i achieve this with javascript? I have searched and found some ways of doing this, but all much less intuitive than this one.
It is not required like in Java or C#. The Javascript arrays grow dynamically, and they are optimized to work that way, so you don't have to set the size of your matrix dimensions upfront.
However, if you insist, you could do something like the following to pre-set the dimensions:
var board = [];
var board_size = 6;
for (var i = 0; i < board_size; i++) {
board[i] = new Array(board_size);
}
So to summarize you just have three options:
Initialization with a literal (like in #Geohut answer)
Initialization with a loop (like in my example)
Do not initialize upfront, but on-demand, closer to the code that access the dimensions.
With JavaScript it is not a static language like Java. It is dynamic. That means you can create the array without a need to preset the size of the array, but if you want you can procreate an array of the size you want.
var items = [[1,2],[3,4],[5,6]];
alert(items[0][0]); // 1
If you need to add to it just do
items.push([7,8]);
That will add the next element.
Code taken from old stack overflow post: How can I create a two dimensional array in JavaScript?
Edited to properly make I in items same as variable declaration.
I'm trying to write a function in javascript that can devide x elements over y elements in every possible combination. I included a picture of what I want to achieve.
It's some basic brute force I guess, but I can't figure out how I should write the loops. I hope somebody can help me.
Thanks in advance
for the code. I don't have much yet, because what I tried didn't work.
But I have a globaly defined empty array which is X long. And I have another array full of THE SAME elements and want every combination of the array of length X containing the elements of array with length Y.
You are looking for combination. In your case n=x and k=y.By borrowing code from here, you can visualize it by this way:
var x = 7;
var y = 3;
comb(y,x).forEach(function(item){
var tr = $('<tr>');
for(var i=0; i<x;++i){
tr.append('<td>');
}
var chunks = item.split(" ");
chunks.pop();
chunks.forEach(function(index){
tr.find("td").eq(+index).addClass("black");
});
$("table").append(tr);
});
DEMO
I'm working with a database that has X and Y points per group, it's being used to draw outlines of images.
Right now in my web side this code is what I use to get the points:
var Drawing = $(XML).find('DrawingXML');
alert($(Drawing[1]).text());
Result:
<DrawingPoints>
<Point><X>1</X><Y>2</Y></Point>
<Point><X>2</X><Y>4</Y></Point>
<Point><X>3</X><Y>5</Y></Point>
<Point><X>2</X><Y>2</Y></Point>
<Point><X>0</X><Y>4</Y></Point>
</DrawingPoints>
Using the .replace() call only changes one item so it's usable for something like this:
.replace("</DrawingPoints>","");
but if I want to replace all 'Point' tags I'm out of luck.
My goal is to use the canvas feature to draw the points out so I want it to be parsed like this:
ctx.beginPath();
ctx.moveTo(1,2);
ctx.lineTo(2,4);
ctx.lineTo(3,5);
ctx.lineTo(2,2);
ctx.lineTo(0,4);
ctx.stroke();
I'm not going to use this with IE browsers just Safari/Chrome, if that helps out.
In this case you'll probably save an awful lot of brainache by using a library instead of writing your own code.
I reckon d3 does what you need:
d3.xml
d3.geo.path
Check out this question/answer. It's not Prototype specific and should help you here.
How to parse XML string with Prototype?
Get all your X and Y values at once:
var points = {};
points.X = Array();
points.Y = Array();
var ix = 0;
$(XML).find('DrawingXML DrawingPoints Point X').each(function()
{
points.X[ix++] = $(this).text();
});
$(XML).find('DrawingXML DrawingPoints Point Y').each(function()
{
points.Y[ix++] = $(this).text();
});
This might not be exact, I didn't test it and my Javascript is a bit rusty, but you get the idea.