javascript check intersections reduce computational complexity - javascript

I have a list where is 500 000 objects, each object has a coordinate. I want to leave only unique objects in that list. In order to do that, I do following:
for (var i = 0; i < features.length; i++) {
for (var j = 0; j < features.length; j++) {
if (features[i].coord == features[j].coord) {
features.splice(features.indexOf(features[j]), 1);
}
}
}
But it is not efficient solution because it is very slow with complexity O(n^2). How can I leave only unique elements in a very fast way?

Use a hash/map/table/whatever to deduplicate; the key (which will have to be a string) is a representation of the coordinate.
function deduplicate(features) {
function magicalToString(coordinate) {
return 'some string representation of the coordinate';
}
var table = {};
for (var i = 0; i < features.length; i++) {
table[magicalToString(features[i].coord)] = features[i];
}
var uniques = [];
// potential optimization: uniques = new Array(Object.keys(table).length)
// not guaranteed to be net faster; benchmark and measure.
for (var k in table) {
if (table.hasOwnProperty(k)) uniques.push(table[k]);
}
return uniques;
}
O(n).

Related

Why is this comparison not working, using google aps scripts?

The code below is not giving me the expected result.
It's to compare rows from two ranges and, although the second range's last row equals the one from the first range, it gives me false as the result.
var entryValuesCom = sheet.getRange(7, 1, LastRowSource, 9).getValues();
var dbDataCom = dbSheet.getRange(2, 1, dbSheet.getLastRow(), 9).getValues();
var entryVlArray = new Array();
var dbArray = new Array();
for (var r = 0; r < entryValuesCom.length; r++) {
if (entryValuesCom[r][0] != '' && entryValuesCom[r][5] != 'Daily Ledger Bal') {
entryVlArray.push(entryValuesCom[r]);
}
}
for (var a = 0; a < dbDataCom.length; a++) {
if (dbDataCom[a][1] != '' && dbDataCom[a][8] == bank) {
dbArray.push(dbDataCom[a]);
}
}
var duplicate = false;
loop1:
for (var x = 0; x < entryVlArray.length; x++) {
loop2:
for (var j = 0; j < dbArray.length; j++) {
if (JSON.stringify(entryVlArray) == JSON.stringify(dbArray)) {
duplicate = true;
break loop1;
}
}
}
Here's a snapshot of how the array is coming:
I've tried it using .join(), but still...
This is for thousands of rows, so is this going to do well performance wise?
I believe your goal as follows.
You want to compare the arrays of entryVlArray and dbArray using Google Apps Script.
When the duplicated rows are existing between entryVlArray and dbArray, you want to output duplicate = true.
Modification points:
When your script is modified, at if (JSON.stringify(entryVlArray) == JSON.stringify(dbArray)) {, all 2 dimensional arrays are compared. I think that this might be the reason of your issue. From your script, I think that it is required to compare each element in the 2 dimensional array.
When above points are reflected to your script, it becomes as follows.
Modified script:
From:
var duplicate = false;
loop1:
for (var x = 0; x < entryVlArray.length; x++) {
loop2:
for (var j = 0; j < dbArray.length; j++) {
if (JSON.stringify(entryVlArray) == JSON.stringify(dbArray)) {
duplicate = true;
break loop1;
}
}
}
To:
var duplicate = false;
for (var x = 0; x < entryVlArray.length; x++) {
for (var j = 0; j < dbArray.length; j++) {
if (JSON.stringify(entryVlArray[x]) == JSON.stringify(dbArray[j])) {
duplicate = true;
break;
}
}
}
console.log(duplicate)
By this modification, when each element (1 dimensional array) in the 2 dimensional array is the same, duplicate becomes true.
Note:
As other method, when an object for searching each row value is prepared, I think that the process cost might be able to be reduced a little. In this case, the script is as follows. Please modify as follows.
From:
var duplicate = false;
loop1:
for (var x = 0; x < entryVlArray.length; x++) {
loop2:
for (var j = 0; j < dbArray.length; j++) {
if (JSON.stringify(entryVlArray) == JSON.stringify(dbArray)) {
duplicate = true;
break loop1;
}
}
}
To:
var obj = entryVlArray.reduce((o, e) => Object.assign(o, {[JSON.stringify(e)]: true}), {});
var duplicate = dbArray.some(e => obj[JSON.stringify(e)]);
References:
reduce()
some()
Added:
About your following 2nd question,
AMAZING!!!! Would there be a way of capturing these duplicates in a pop up, using reduce() and some()?
When you want to retrieve the duplicated rows, how about the following script? In this case, I thought that filter() is useful instead of some().
Modified script:
var obj = entryVlArray.reduce((o, e) => Object.assign(o, {[JSON.stringify(e)]: true}), {});
// var duplicate = dbArray.some(e => obj[JSON.stringify(e)]);
var duplicatedRows = dbArray.filter(e => obj[JSON.stringify(e)]);
console.log(duplicatedRows)
In this modified script, you can see the duplicated rows at the log.
About a pop up you expected, if you want to open a dialog including the duplicated rows, how about adding the following script after the line of var duplicatedRows = dbArray.filter(e => obj[JSON.stringify(e)]);?
Browser.msgBox(JSON.stringify(duplicatedRows));

Most performant way to build object from array of objects

Currently, I'm building an object from an array of objects with the following:
var popOverOptions = {
defaultOption: true
}
if (a) {
options.push({a: "b"});
}
if (c) {
options.push({c: "d"});
}
// Loop through array of objects
for (var i = 0; i < options.length; i++) {
// Add objects in array to popoverOptions object
for (key in options[i]) {
popoverOptions[key] = options[i][key]
}
}
I'm thinking this could be optimized and I'm curious if there is a better way to write this, possibly using .reduce(), .forEach() or some other method.
In ECMAScript 6, you can use
Object.assign to copy the properties to popOverOptions
The spread operator to expand the options array.
Object.assign(popOverOptions, ...options);
You can optimise the loop itself like this:
for (var i=0, n=options.length; i<n; ++i) {
This reduces the number of times you need to access options.length which is slower than reading it from a local.
A smaller optimisation:
for (var i=options.length-1; i--; /* empty */) {
Source: http://www.phpied.com/extreme-javascript-optimization/
You can cache length for a slight speed improvement.
If the keys in your array are identical for each element, you can get twice the speed (in Chrome) by caching Object.keys(options[0]), and iterating through that.
Even if the keys aren't the same, you can still get approx. 25% increase in speed by iterating through Object.keys(options[i]).
var options= [];
for(var i = 0 ; i <= 1000 ; i++) { //create array of objects with random numbers
options[i]= {};
for(var j = 0 ; j <= 5000 ; j++) {
options[i][j]= Math.random();
}
}
var popOverOptions = {};
function keyin() {
var timer= new Date();
for(var i = 0, leni = options.length ; i < leni; i++) {
for(var key in options[i]) {
popOverOptions[key] = options[i][key];
}
}
alert((new Date()-timer)/1000+' seconds');
} //keyin
function objkeys(same) {
var timer= new Date(),
keys= Object.keys(options[0]);
for(var i = 0, leni = options.length ; i < leni; i++) {
if(!same) keys= Object.keys(options[i]);
for(var key = 0, lenk = keys.length ; key < lenk ; key++) {
popOverOptions[keys[key]] = options[i][keys[key]];
}
}
alert((new Date()-timer)/1000+' seconds');
} //objkeys
<button onclick="keyin()">key in options</button><br>
<button onclick="objkeys(true)">Object.keys, same keys per element</button><br>
<button onclick="objkeys(false)">Object.keys, different keys per element</button>

finding an array in another Multidimensional array in javascript

I'm writing a simple snakes game using JavaScript and HTML5 canvas.
I have a Multidimensional array that hold snake block like this:
snake=[[1,1],[1,2]];
and set it on arrayMap using (snake.indexOf([i],[j])!=-1) then draw arrayMap on canvas.
for (var i = 0; i < blocksHeightCount; i++) {
for (var j = 0; j < blocksWidthCount; j++) {
if ((snake.indexOf(i,j)!=-1)||
(walls.indexOf(i,j)!=-1)||
(foods.indexOf(i,j)!=-1)) {
arrayMap[i][j]=1;
} else {
arrayMap[i][j]=0;
}
}
}
for (var i = 0; i < blocksHeightCount; i++) {
for (var j = 0; j < blocksWidthCount; j++) {
Block = arrayMap[i][j];
if (Block!=0){
ctx.fillStyle = (Block != 9) ? colors[Block]
: "#bdc3c7";
ctx.fillRect(j * cubeWidth, i * cubeHeight
, cubeWidth-.4,cubeHeight-.4);
}
}
}
the problem is indexOf isn't working when I set array on it!
It works fine when I set indexOf("i,j") but i need it to be array.
please help, thx
First solution : using Array.map
Each element of your arrays snake, walls and foods is an array with 2 elements. So to check if an [x,y] exists in one of the arrays you need a simple way to
compare two arrays [x1, y1] and [x2, y2]. Comparing arrays directly using the operator == will compare their references and not values (Thanks #Elena for the remarque). A way to compare values
would be to affect a hash to each array and compare hashes. By hash I mean a number which is unique for each array of type [x, y]. That could be x * blocksWidthCount + y
in your case and the code will be :
function getHash(x, y){
return x * blocksWidthCount + y;
}
var blockHashes = snake.concat(walls).concat(foods).map(function(cell) {
return getHash(cell[0], cell[1]);
}); // hashes of all blocks in one array
for (var i = 0; i < blocksHeightCount; i++) {
for (var j = 0; j < blocksWidthCount; j++) {
if (blockHashes.indexOf(getHash(i, j)) != -1) {
arrayMap[i][j]=1;
} else {
arrayMap[i][j]=0;
}
}
}
Second Solution Changing the way we see things
Instead of looping over all cells and verifying if every single cell is a block which gives a complexity of O(N * M) (N number of cells and M number of blocks).
We can do better simply by supposing that there is no block and then loop over blocks and mark them as blocks which is in O(N + M) !
function markBlock(cell){
arrayMap[cell[0]][cell[1]] = 1;
}
for (var i = 0; i < blocksHeightCount; i++)
for (var j = 0; j < blocksWidthCount; j++)
arrayMap[i][j] = 0;
snake.forEach(markBlock);
walls.forEach(markBlock);
foods.forEach(markBlock);

loop on Multiple array values

I am confused about how to iterate on multiple values.
for example : values.categories[0].num[0].entry[0].label;
Do I need to write three for loops in order to iterate through categories, num and entry.
Because categories[0] will always identify the 1st position, but am looking for generic categories[i].
Can you please help me out whether to write three for loops or better option is there to achieve.?
This is what I have tried:
var result = [];
for (var i = 0; i < categories.length; i++) {
var abc = categories[i].num;
for (var j = 0; j < abc.length; j++){
var def = num[i].entry;
}
for(var k = 0; k < def.length; k++){
var ghi = entry[i].label;
result.push(ghi)
console.log(result);
}
}
you can use the each function of jquery.
$.each(categories, function(ci, num) {
// This set the index of the array in ci and the value in num = categories[ci]
$.each(num, function(ni, entry) {
// etc ...
});
});
if you want it to stop the iteration you can return false inside the callback function.

How should I represent data for effective searching and comparing strings

I have two array with length 300. They look like this (JSON representation):
[
[
["word1",0.000199],
["word2",0.000102],
...
["word15",0.000102]
],
...
[
["anotherword1",0.0032199],
["anotherword2",0.032302],
...
["anotherword15",0.0320102]
]
]
And I have this bruteforce algorithm:
for(var i = 0; i < 300; i++)
{
for(var j = 0; j < 15; j++)
{
for(var ii = i + 1; ii < 300; ii++)
{
for(var jj = 0; jj < 15; jj++)
{
for(var jjj = 0; jjj < 15; jjj++)
{
if(new_keywords[i][j][0] === new_keywords[ii][jj][0] && new_keywords[ii][jj][0] === state_keywords[i][jjj][0])
{
console.log(0);
}
}
}
}
}
}
I need to search for same words in those arrays and if words are the same, then I sum values and divide sum by 3 and replace that value in state_keywords array. So for each word which is more then once in array I have means of its values.
Now... my approach is very bad because I have now about 300 mil iterations and that is crazy. I need some better implementation of my array in JavaScript. Something like lexikographical tree or kd-tree or something.
Thank you.
EDIT:
Here is http://jsfiddle.net/dD7yB/1/ with example.
EDIT2:
I'm sorry if I'm not clear enough. So what exaclty I'm doing:
I have array state_keywords. Indexes are from 0 to 299 and they representing a themes...
Each theme may be represented by 15 words and every time new_keywords array arrives, they may be different.
When new_keywords array arrive I need to check every word in that array if it is in state_keywords array on same theme index.
If it is: add probabilities up and divide by 2.
If it is not: add new word into state_keyword array BUT if they are more than 15 words for one theme (which now are) I need to store just first 15 sorted by probabilities.
And this I need to do as effectively as possbile, because I need to do this every second so it must be FAST.
EDIT3:
Now I use this code:
var i, j, jj, l;
for(i = 0; i < 300; i++)
{
for(j = 0; j < 15; j++)
{
l = new_keywords[i].length;
for(jj = 0; jj < l; jj++)
{
if(state_keywords[i][j][0] === new_keywords[i][jj][0])
{
state_keywords[i][j][1] = (state_keywords[i][j][1] + new_keywords[i][jj][1]) / 2;
}
}
}
}
which is much faster then the previous one.
Why don't you make those arrays into objects with the strings as keys to the values? Then you can just just look up the words directly and get the values?
var wordlists = [
{
"word1":0.000199,
"word2":0.000102,
...
"word15":0.000102
},
...
{
"anotherword1":0.0032199,
"anotherword2":0.032302,
...
"anotherword15":0.0320102
}
]
and then lookup with
wordlists[0]["word2"] //0.000102

Categories

Resources