Increment counts of sites visited including TLDs and subdomains outputted to JSON - javascript
So I was asked to create an algorithm that when given a basic input of an array of counts and sites, it will output the accumulated visits to each TLD and Subdomain represented in a JSON object that will yield data like:
1120 com
800 google.com
310 reddit.com
60 mail.yahoo.com
10 mobile.sports.yahoo.com
50 sports.yahoo.com
10 stackoverflow.com
3 org
3 wikipedia.org
2 en.wikipedia.org
2 es.wikipedia.org
1 mobile.sports
1 sports
The input is something like:
// visits = [ "800,google.com",
// "60,mail.yahoo.com",
// "10,mobile.sports.yahoo.com",
// "40,sports.yahoo.com",
// "310,reddit.com",
// "10,stackoverflow.com",
// "2,en.wikipedia.org",
// "1,es.wikipedia.org",
// "1,mobile.sports" ]
My code looks like this so far and I know its wrong, but my brain is melted at the moment and I am not sure how to proceed. I am not necessarily looking for you to write the algorithm for me, but I do want to understand logically how I could break this down.
function getDomainHits(arr){
var splitCount = [];
var splitDomains = [];
var domainCountDict = {"Domains" : [],"Count" : 0};
for (var i = 0; i < arr.length; i++){
splitCount = arr[i].split(",");
splitDomains = splitCount[1].split(".");
for (var j = 0; j < splitDomains.length; j++){
if (!domainCountDict.Domain.includes(splitDomains[j])){
domainCountDict.Domain.push(splitDomains[j]);
}
}
}
console.log(domainCountDict);
}
As you can see I stopped here because I couldn't think of the best way to split these into different key, value pairs - one being domains and the other being the counts. Also my algorithm doesn't exactly follow the requirements.
So I figured out the algorithm. Define a variable - initialize it as an Array, and a dictionary to store the processed array data.
var splitCount = [];
var domainCountDict = {};
Then you need to take the Array of strings (arr - the function parameter) and iterate through it. On each iteration you need to split the string element into another Array to further process it.
for (var i = 0; i < arr.length; i++){
splitCount = arr[i].split(",");
...
}
So for the example input data of
// visits = [ "800,google.com",
// "60,mail.yahoo.com",
// "10,mobile.sports.yahoo.com",
// "40,sports.yahoo.com",
// "310,reddit.com",
// "10,stackoverflow.com",
// "2,en.wikipedia.org",
// "1,es.wikipedia.org",
// "1,mobile.sports" ]
Iteration 0 would be split into an Array of ["800","google.com"] and assigned to Var splitCount. You would then need to access splitCount and because of the input formatting you don't need to create a for loop. I created a variable to store the current count of the site - which will always be element 0 because of the format of the input data.
I didn't bother with input sanitation here because I didn't have time to create a map function that will turn the number elements into - well... numbers. I relied on the assumption that the input data will always have a number in the 0th index - which is terrible. Don't do this.
var curCnt = 0;
if (splitCount[0]){
curCnt = splitCount[0];
}
This next chunk of logic hurt my brain a little bit because I needed to find a way to store each domain component and its count in the dict and determine if the other domains contained components that already existed and if so increment those. Lets make some more Arrays!
var domain = [];
var currentDom = [];
if (splitCount[1] != undefined && splitCount[1]){
domain = splitCount[1].split(".");
for (var j = domain.length - 1; j >= 0; j--){
...
}
}
Above you will see that created an Array to hold the domain components called domain and another called currentDom to hold the components that are being worked and have already been worked, because we want to make sure that we count com and google.com. Lets look inside of the for loop.
for (var j = domain.length - 1; j >= 0; j--){
currentDom.unshift(domain.pop());
/*console.log("current iter: " + k + "\n"
+ "currentDom: " + currentDom.join(".") + "\n"
+ "current count: " + curCnt + "\n");*/
if (currentDom.join(".") in domainCountDict){
/*console.log("currentDom2: " + currentDom.join("."));
console.log("increment existing");*/
domainCountDict[currentDom.join(".")] += parseInt(curCnt);
}
if (!(currentDom.join(".") in domainCountDict)){
/*console.log("currentDom3: " + currentDom.join("."));
console.log("increment new");*/
domainCountDict[currentDom.join(".")] = parseInt(curCnt);
//console.log(domainCountDict);
}
}
Above you will see that I am iterating backwards in this loop to work the TLD first and then the domains/subdomains. I chose to pop the last element off the end of the current array and unshift it to the beginning of the new Array, currentDom. This will effectively let me work on a portion of the entire FQDN to determine if it has been included in the dictionary.
I have a few if statements to determine if the currentDom is included in the array. I had to use Array.join() to accurately check if the string of the current domain components have been included in the dictionary. If not then the string of currentDom would be added as a key and the curCnt would be the value assigned. If so, then the value would be incremented. Because of my lazy input sanitation in the curCnt assignment I had to parse these as Int because JS dynamic types. I am sure there is a better way, but my brain hurts now.
Finally make sure that you return the created dictionary on the outside of all of these for loops.
The full algorithm is below
// Sample output (in any order/format):
// getTotalsByDomain(counts)
// 1320 com
// 900 google.com
// 410 yahoo.com
// 60 mail.yahoo.com
// 10 mobile.sports.yahoo.com
// 50 sports.yahoo.com
// 10 stackoverflow.com
// 3 org
// 3 wikipedia.org
// 2 en.wikipedia.org
// 1 es.wikipedia.org
// 1 mobile.sports
// 1 sports
let counts = [ "900,google.com",
"60,mail.yahoo.com",
"10,mobile.sports.yahoo.com",
"40,sports.yahoo.com",
"300,yahoo.com",
"10,stackoverflow.com",
"2,en.wikipedia.org",
"1,es.wikipedia.org",
"1,mobile.sports" ];
console.log(getDomainHits(counts));
function getDomainHits(arr){
var splitCount = [];
var domainCountDict = {};
for (var i = 0; i < arr.length; i++){
splitCount = arr[i].split(",");
var curCnt = 0;
if (splitCount[0]){
curCnt = splitCount[0];
}
var domain = [];
var currentDom = [];
if (splitCount[1] != undefined && splitCount[1]){
domain = splitCount[1].split(".");
for (var j = domain.length - 1; j >= 0; j--){
currentDom.unshift(domain.pop());
/*console.log("current iter: " + k + "\n"
+ "currentDom: " + currentDom.join(".") + "\n"
+ "current count: " + curCnt + "\n");*/
if (currentDom.join(".") in domainCountDict){
/*console.log("currentDom2: " + currentDom.join("."));
console.log("increment existing");*/
domainCountDict[currentDom.join(".")] += parseInt(curCnt);
}
if (!(currentDom.join(".") in domainCountDict)){
/*console.log("currentDom3: " + currentDom.join("."));
console.log("increment new");*/
domainCountDict[currentDom.join(".")] = parseInt(curCnt);
//console.log(domainCountDict);
}
}
}
}
return domainCountDict;
}
Related
Passing hands of cards around a table in Javascript
This seems like a stupidly simple thing to do, but I can't figure out what I'm doing wrong. The goal is to have an array of 8 other arrays, which each contain hands of cards (in the example, the arrays just contain arbitrary numbers). Then, depending on whether passDirection is set to -1 or 1, each array is cycled through and replaced with the one next to it. The desired end result is that the values of playerList essentially shift by 1 either up or down, and this can be repeated several times without issue. What's actually happening with the code I have below, though, is that all the arrays are just being replaced with what's at index 0, except for the first one. How can I fix this? var playerList = new Array; var passDirection = -1; for(i = 0; i < 8; i++) { playerList.push([playerList.length,i]); // Fill Arrays with arbitrary data } for (i=0; i< playerList.length; i++) { console.log(i + ": " + playerList[i]); // Check their values before anything is done to them } for(q=0; q < 5; q++){ // Repeat the process 5 times, just because. var bufferArray = playerList[0]; // Put Array Element 0's value in a buffer as it will be replaced first for(i = 0; i < playerList.length && i > (playerList.length * -1); i += passDirection) { var catcher = i; // 'catcher' should be the array that gets replaced var passer = catcher - passDirection; // 'passer' should be the one it gets replaced with if (catcher < 0) { catcher = catcher + playerList.length; } if (passer < 0) { passer = passer + playerList.length; } else if (passer >= playerList.length) { passer = passer - playerList.length; } if (passer == 0) { playerList[catcher] = bufferArray; } else { playerList[catcher] = playerList[passer]; } } for (i=0; i< playerList.length; i++) { console.log(i + ": " + playerList[i]); } console.log("..."); } https://jsfiddle.net/3r1Lhwc5
You have two errors in your code: if (passer = 0) is performing an assignment. You need if (passer === 0). The passer index is looking at the wrong side of the value. Currently you are first getting from 1 and putting at 0, then getting from 0 and putting at 7 (i.e. -1). Notice how you are moving the same value in the second iteration. You need to change passer = catcher - passDirection to passer = catcher + passDirection Note that all this can be done much easier with the splice, shift, unshift, pop and push Array methods (on the main playerList).
You can make your life easier by using Array methods to move elements from the beginning to end of an array or vice versa. Using this in the body of your for loop should do the trick: if (passDirection === -1) { const first = playerList.shift(); playerList.push(first); } else { const last = playerList.pop(); playerList.unshift(last); }
Pull information from a data file?
I have over 170000 entries of data in data file in this format: 1479661:-1,1,-1,-898,-769,0.00;-1,2,-1,-96,-1402,0.00;-1,3,-1,117,-1397,0.00;-1,4,-1,-4,-2420,0.00;4,5,-1,5570,4395,0.00;4,6,-1,5570,4395,0.00;4,7,-1,5570,4395,0.00;4,8,-1,5570,4395,0.00;4,9,-1,5570,4395,0.00;4,10,-1,5570,4395,0.00;4,11,-1,5570,4395,0.00;4,12,-1,5570,4395,0.00;4,13,-1,5570,4395,0.00;4,14,-1,5570,4395,0.00;-1,15,-1,913,-3533,0.00;4,16,-1,5570,4395,0.00;4,17,-1,5570,4395,0.00;4,18,-1,5570,4395,0.00;4,19,-1,5570,4395,0.00;4,20,-1,5570,4395,0.00;4,21,-1,5570,4395,0.00;4,22,-1,5570,4395,0.00;4,23,-1,5570,4395,0.00;4,24,-1,5570,4395,0.00;4,25,-1,5570,4395,0.00;4,26,-1,5570,4395,0.00;4,27,-1,5570,4395,0.00;4,28,-1,5570,4395,0.00;4,29,-1,5570,4395,0.00;:117,-1397,7,7.00,A,Dead;: 1479662:-1,1,-1,-898,-769,0.00;-1,2,-1,-96,-1402,0.00;-1,3,-1,117,-1392,0.00;-1,4,-1,-6,-2419,0.00;4,5,-1,5570,4395,0.00;4,6,-1,5570,4395,0.00;4,7,-1,5570,4395,0.00;4,8,-1,5570,4395,0.00;4,9,-1,5570,4395,0.00;4,10,-1,5570,4395,0.00;4,11,-1,5570,4395,0.00;4,12,-1,5570,4395,0.00;4,13,-1,5570,4395,0.00;4,14,-1,5570,4395,0.00;-1,15,-1,913,-3533,0.00;4,16,-1,5570,4395,0.00;4,17,-1,5570,4395,0.00;4,18,-1,5570,4395,0.00;4,19,-1,5570,4395,0.00;4,20,-1,5570,4395,0.00;4,21,-1,5570,4395,0.00;4,22,-1,5570,4395,0.00;4,23,-1,5570,4395,0.00;4,24,-1,5570,4395,0.00;4,25,-1,5570,4395,0.00;4,26,-1,5570,4395,0.00;4,27,-1,5570,4395,0.00;4,28,-1,5570,4395,0.00;4,29,-1,5570,4395,0.00;:117,-1392,7,7.07,A,Dead;: Now each data array starts with a unique id and i was wondering is there a convenient way to push each entry 1479661 then 1479662 every second to an array and assign the values within the unique id into 6 fields that update. Now I ask if there is a more convenient way as currently I am using this method: Data comprises of three chunks in a single line Split the link into chunks var chunkOne = [1479661]; var chunkTwo = [-1,1,-1,-898,-769,0.00,-1,2,-1,-96,-1402,0.00,-1,3,-1,117,-1397,0.00,-1,4,-1,-4,-2420,0.00,4,5,-1,5570,4395,0.00,4,6,-1,5570,4395,0.00,4,7,-1,5570,4395,0.00,4,8,-1,5570,4395,0.00,4,9,-1,5570,4395,0.00,4,10,-1,5570,4395,0.00,4,11,-1,5570,4395,0.00,4,12,-1,5570,4395,0.00,4,13,-1,5570,4395,0.00,4,14,-1,5570,4395,0.00,-1,15,-1,913,-3533,0.00,4,16,-1,5570,4395,0.00,4,17,-1,5570,4395,0.00,4,18,-1,5570,4395,0.00,4,19,-1,5570,4395,0.00,4,20,-1,5570,4395,0.00,4,21,-1,5570,4395,0.00,4,22,-1,5570,4395,0.00,4,23,-1,5570,4395,0.00,4,24,-1,5570,4395,0.00,4,25,-1,5570,4395,0.00,4,26,-1,5570,4395,0.00,4,27,-1,5570,4395,0.00,4,28,-1,5570,4395,0.00,4,29,-1,5570,4395,0.00]; var chunkThree = [117,-1397,7,7.00,"A","Dead"]; Then get length of each array: var chunkOneLength = chunkOne.length; var chunkTwoLength = chunkTwo.length; var chunkThreeLength = chunkThree.length; Pick out the nth value in the array depending on the data chunk: //uniqueID set as first for (var i = 0; i < chunkOneLength; i = i + 1) { // useful code would go here alert("This is the unique ID " + chunkOne[i]); } //teamval for (var i = 0; i < chunkTwoLength; i = i + 6) { // useful code would go here alert("This is the teamVal " + chunkTwo[i]); } Now the only problem I see with this method, is that the original data array will need to be formatted and separated into chunks every time.
As you have a separator on each section i.e. : you can actually use split to split them up like below and push them into a array and only have to do 2 loops - firstly pushing the split data in a new array and then looping through them and populating the structure. Please note as long as each chunk of data is separated with : this will work with anything even if you expand the data later. obviously it will not work if you remove the : separator. example below: var splitChucks = []; var chucks = [ "1479661:-1,1,-1,-898,-769,0.00;-1,2,-1,-96,-1402,0.00;-1,3,-1,117,-1397,0.00;-1,4,-1,-4,-2420,0.00;4,5,-1,5570,4395,0.00;4,6,-1,5570,4395,0.00;4,7,-1,5570,4395,0.00;4,8,-1,5570,4395,0.00;4,9,-1,5570,4395,0.00;4,10,-1,5570,4395,0.00;4,11,-1,5570,4395,0.00;4,12,-1,5570,4395,0.00;4,13,-1,5570,4395,0.00;4,14,-1,5570,4395,0.00;-1,15,-1,913,-3533,0.00;4,16,-1,5570,4395,0.00;4,17,-1,5570,4395,0.00;4,18,-1,5570,4395,0.00;4,19,-1,5570,4395,0.00;4,20,-1,5570,4395,0.00;4,21,-1,5570,4395,0.00;4,22,-1,5570,4395,0.00;4,23,-1,5570,4395,0.00;4,24,-1,5570,4395,0.00;4,25,-1,5570,4395,0.00;4,26,-1,5570,4395,0.00;4,27,-1,5570,4395,0.00;4,28,-1,5570,4395,0.00;4,29,-1,5570,4395,0.00;:117,-1397,7,7.00,A,Dead;:", "1479662:-1,1,-1,-898,-769,0.00;-1,2,-1,-96,-1402,0.00;-1,3,-1,117,-1392,0.00;-1,4,-1,-6,-2419,0.00;4,5,-1,5570,4395,0.00;4,6,-1,5570,4395,0.00;4,7,-1,5570,4395,0.00;4,8,-1,5570,4395,0.00;4,9,-1,5570,4395,0.00;4,10,-1,5570,4395,0.00;4,11,-1,5570,4395,0.00;4,12,-1,5570,4395,0.00;4,13,-1,5570,4395,0.00;4,14,-1,5570,4395,0.00;-1,15,-1,913,-3533,0.00;4,16,-1,5570,4395,0.00;4,17,-1,5570,4395,0.00;4,18,-1,5570,4395,0.00;4,19,-1,5570,4395,0.00;4,20,-1,5570,4395,0.00;4,21,-1,5570,4395,0.00;4,22,-1,5570,4395,0.00;4,23,-1,5570,4395,0.00;4,24,-1,5570,4395,0.00;4,25,-1,5570,4395,0.00;4,26,-1,5570,4395,0.00;4,27,-1,5570,4395,0.00;4,28,-1,5570,4395,0.00;4,29,-1,5570,4395,0.00;:117,-1392,7,7.07,A,Dead;:"]; for (var i = 0; i < chucks.length; i++){ splitChucks.push(chucks[i].split(':')) } for (var h = 0; h < splitChucks.length; h++) { alert("This is the unique ID " + splitChucks[h][0]); alert("This is the teamVal " + splitChucks[h][1]); } Hope this helps, this is a much more efficient way to do your task :)
var splitChucks = []; var chucks = [ "1479661:-1,1,-1,-898,-769,0.00;-1,2,-1,-96,-1402,0.00;-1,3,-1,117,-1397,0.00;-1,4,-1,-4,-2420,0.00;4,5,-1,5570,4395,0.00;4,6,-1,5570,4395,0.00;4,7,-1,5570,4395,0.00;4,8,-1,5570,4395,0.00;4,9,-1,5570,4395,0.00;4,10,-1,5570,4395,0.00;4,11,-1,5570,4395,0.00;4,12,-1,5570,4395,0.00;4,13,-1,5570,4395,0.00;4,14,-1,5570,4395,0.00;-1,15,-1,913,-3533,0.00;4,16,-1,5570,4395,0.00;4,17,-1,5570,4395,0.00;4,18,-1,5570,4395,0.00;4,19,-1,5570,4395,0.00;4,20,-1,5570,4395,0.00;4,21,-1,5570,4395,0.00;4,22,-1,5570,4395,0.00;4,23,-1,5570,4395,0.00;4,24,-1,5570,4395,0.00;4,25,-1,5570,4395,0.00;4,26,-1,5570,4395,0.00;4,27,-1,5570,4395,0.00;4,28,-1,5570,4395,0.00;4,29,-1,5570,4395,0.00;:117,-1397,7,7.00,A,Dead;:", "1479662:-1,1,-1,-898,-769,0.00;-1,2,-1,-96,-1402,0.00;-1,3,-1,117,-1392,0.00;-1,4,-1,-6,-2419,0.00;4,5,-1,5570,4395,0.00;4,6,-1,5570,4395,0.00;4,7,-1,5570,4395,0.00;4,8,-1,5570,4395,0.00;4,9,-1,5570,4395,0.00;4,10,-1,5570,4395,0.00;4,11,-1,5570,4395,0.00;4,12,-1,5570,4395,0.00;4,13,-1,5570,4395,0.00;4,14,-1,5570,4395,0.00;-1,15,-1,913,-3533,0.00;4,16,-1,5570,4395,0.00;4,17,-1,5570,4395,0.00;4,18,-1,5570,4395,0.00;4,19,-1,5570,4395,0.00;4,20,-1,5570,4395,0.00;4,21,-1,5570,4395,0.00;4,22,-1,5570,4395,0.00;4,23,-1,5570,4395,0.00;4,24,-1,5570,4395,0.00;4,25,-1,5570,4395,0.00;4,26,-1,5570,4395,0.00;4,27,-1,5570,4395,0.00;4,28,-1,5570,4395,0.00;4,29,-1,5570,4395,0.00;:117,-1392,7,7.07,A,Dead;:"]; for (var i = 0; i < chucks.length; i++){ splitChucks.push(chucks[i].split(':')) } for (var h = 0; h < splitChucks.length; h++) { alert("This is the unique ID " + splitChucks[h][0]); alert("This is the teamVal " + splitChucks[h][1]); }
One of the best purposes of an unique ID is to act as an index. So, instead of iterating over your index array (chunkOne), use ID as an Object key! // 1479661:-1,1,-1,-898,-769,0.00;-1,2,-1,-96,-1402,0.00;-1,3,-1,117,-1397,0.00;-1,4,-1,-4,-2420,0.00;4,5,-1,5570,4395,0.00;4,6,-1,5570,4395,0.00;4,7,-1,5570,4395,0.00;4,8,-1,5570,4395,0.00;4,9,-1,5570,4395,0.00;4,10,-1,5570,4395,0.00;4,11,-1,5570,4395,0.00;4,12,-1,5570,4395,0.00;4,13,-1,5570,4395,0.00;4,14,-1,5570,4395,0.00;-1,15,-1,913,-3533,0.00;4,16,-1,5570,4395,0.00;4,17,-1,5570,4395,0.00;4,18,-1,5570,4395,0.00;4,19,-1,5570,4395,0.00;4,20,-1,5570,4395,0.00;4,21,-1,5570,4395,0.00;4,22,-1,5570,4395,0.00;4,23,-1,5570,4395,0.00;4,24,-1,5570,4395,0.00;4,25,-1,5570,4395,0.00;4,26,-1,5570,4395,0.00;4,27,-1,5570,4395,0.00;4,28,-1,5570,4395,0.00;4,29,-1,5570,4395,0.00;:117,-1397,7,7.00,A,Dead;: var data = { 1479661: [ -1,1,-1,-898,-769,0.00,-1,2,-1,-96,-1402,0.00,-1,3,-1,117,-1397,0.00,-1,4,-1,-4,-2420,0.00,4,5,-1,5570,4395,0.00,4,6,-1,5570,4395,0.00,4,7,-1,5570,4395,0.00,4,8,-1,5570,4395,0.00,4,9,-1,5570,4395,0.00,4,10,-1,5570,4395,0.00,4,11,-1,5570,4395,0.00,4,12,-1,5570,4395,0.00,4,13,-1,5570,4395,0.00,4,14,-1,5570,4395,0.00,-1,15,-1,913,-3533,0.00,4,16,-1,5570,4395,0.00,4,17,-1,5570,4395,0.00,4,18,-1,5570,4395,0.00,4,19,-1,5570,4395,0.00,4,20,-1,5570,4395,0.00,4,21,-1,5570,4395,0.00,4,22,-1,5570,4395,0.00,4,23,-1,5570,4395,0.00,4,24,-1,5570,4395,0.00,4,25,-1,5570,4395,0.00,4,26,-1,5570,4395,0.00,4,27,-1,5570,4395,0.00,4,28,-1,5570,4395,0.00,4,29,-1,5570,4395,0.00,117,-1397,7,7.00,"A","Dead" ] // More data... }; Object.keys(data).forEach(function(key) { console.log('This is ID ' + key); data[key].forEach(function(value,index,array) { console.log('Index ' + index + ' of data key ' + key + ':'); console.log(data[key][index]); }); }); Test this on any modern browser's console or node.js instance to see results.
How to push into the second dimension of a two dimensional array in JavaScript?
I have a list of players in denoted as activeRange[x] where x will vary from day-to-day. Each of the x values will have to have AT LEAST 4 more subsequent values (likely a bit more). Ideally I'd like the array to look like: activeRange[x][y] So here's what I've done so far: var MATCH = AllData[TotalRows][TotalColumns+1]; activeRange[TotNumPlayers].push(MATCH); This is all located within 3 nested for loops. TotNumPlayers will iterate through a given set declared at the beginning (somewhat like 23). Once done, the TotalRows will iterate, then finally TotalColumns I'm running into the following error: TypeError: Cannot find function push in object mitch mitch is the value of activeRange[0]. I've been staring at this way too long, so any help would be appreciated! EDIT: Code inserted below: PLEASE IGNORE ALL THE COMMENTS. I COPY/PASTED THIS FROM A BIT OF CODE I USED YESTERDAY TO PERFORM A DIFFERENT FUNCTION. This is the second time I've ever posted on this website, so trying to format this monster to be pretty was scary sounding. Hopefully this is good enough. This is how activeRange was declared and initialized. var activeRange = new Array(); for (var b=0; b<=lastRow-2; b++){ activeRange[b] = sheetRANK.getRange(b+2,1).getValue(); } This is the function. function getTotalScore(activeRange, w) { Logger.clear() var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheetWAR = ss.getSheetByName('WAR'); var sheetRANK = ss.getSheetByName('RANK'); var AllData = sheetRANK.getDataRange().getValues(); Logger.log('First'); for (var TotNumPlayers = 0; TotNumPlayers <= activeRange.length; TotNumPlayers++) { Logger.log('Second'); var f = 0; for (var TotalColumns = 0; TotalColumns <= AllData[0].length; ++TotalColumns) { // Init n. If n <= the total columns (second dimension), inc n. Logger.log('Third'); for (var TotalRows = 0; TotalRows <= AllData.length; ++TotalRows) { // Init i. If i <= the total rows (first dimension), inc i. Logger.log('Fourth'); //try{ // to avoid errors. if (activeRange[TotNumPlayers] != "") { Logger.log('Here?'); if (AllData[TotalRows][TotalColumns].valueOf().toUpperCase() == activeRange[TotNumPlayers].toUpperCase()) { Logger.log('How About Here?'); var MATCH = AllData[TotalRows][TotalColumns + 1]; activeRange.push(TotNumPlayers, MATCH); for (var Calc = 0; Calc <= activeRange[TotNumPlayers].length - 1; Calc++) { var OverallScore = ((activeRange[TotNumPlayers][0] * 1.0) + (activeRange[TotNumPlayers][1] * .75) + (activeRange[TotNumPlayers][2] * .50) + (activeRange[TotNumPlayers][3] * .25)); sheetRANK.getRange(activeRange[TotNumPlayers] + 1, 2).setValue(OverallScore); f = f + 1; } if (TotalRows == AllData.length - 1 && TotalColumns == AllData[0].length - 1 && f == 0) { Browser.msgBox('No names matching \'' + activeRange[TotNumPlayers] + '\' found. Check your spelling!'); return; } } } } } } }
Try thinking about what kind of data structures you can use to make your life easier. For this particular case, you have a list of players that you want to associate some data with. You'd probably use a structure like: activeRange = [ { name: 'mitch', data: [] } ] When you want to update the data, you'd simply call activeRange[0].data.push(someData). activeRange is an array of players and each player is represented by an object with some properties, (name, data, etc). Calling activeRange[0] yields the first player in your array and activeRange[0].data will yield the data associated with that player, which you can then manipulate however you want (push, pop, etc)
Based on your comments, you need a structure more like this var activeRange = [ { name: 'mitch', otherData: [ 10, 11, 12, 13 ] }, { name: 'viper', otherData: [ //values ] } ] you can access that by activeRange[0].otherData[2] to add to it, just push into the sub array activeRange[0].otherData.push(newValue)
Finding the rank of the Given string in list of all possible permutations
I am trying to find the Rank of the given string in the list of possible permutations. I tried to come up with a solution that tries to find all possible permutations, assign a rank to them and then display it. But this drastically reduces the performance when the length of the string keeps increasing. So was wondering if someone can think of an efficient solution for this problem.. function permute(str) { // Sort the string var arr = []; for (var i = 0; i < str.length; i++) arr.push(str[i]); var sortedString = arr.sort().join(''), // Length of the string length = str.length, used = []; // Create a boolean array for length of the string while (length--) used.push(false); // String buffer that holds the current string var out = ''; // Call the function doPermute(sortedString, str, out, used, str.length, 0); } var count = 0; function doPermute(inp, givenString, out, used, length, level) { // Only if length of the string equal to current level print it // That is permutation length is eqaul to string length if (level == length) { count++; //console.log('Perm :: ' + out + ' -- ' + count); if (out === givenString) { var pp = 'Rank of :: ' + out + ' -- ' + count; $('div').append('<p>' + pp + '</p>'); } return; } for (var i = 0; i < length; ++i) { // If variable used continue if (used[i]) continue; // Append the current char in loop out += inp[i]; // set variable to true used[i] = true; // Call the function again doPermute(inp, givenString, out, used, length, level + 1); // Set it to false as the variable can be reused used[i] = false; // remove the last character of the buffer out = out.slice(0, out.length - 1) } } permute('dbcarf') Fiddle
Sure: if input string is "cab". What is the lowest rank that a string starting with c could get? c Note the strings that come before it. abc acb bac bca So a string starting with c has minimum rank 5.This is just number of characters in input string that come lexicographically before c.(in order a,b,c,d,e,f...)So we have 2.Each word starting with a letter can have 2 words. Next letter is "a"? What is minimum rank that a word starting with "ca" can get? 5 Why? "a" is the best way we can fill the second spot with the remaining letters. And the same goes for third element "b". So rank of "cab" is 5. In general.(Assuming no duplicates, though this is not much harder) var W; //input string var C[26]; var rank = 1; for (var i = 0; i < W.length; i++) C[W[i] - 'a']++; for (var i = 0; i < W.length; i++) { //How many characters which are not used, that come before current character var count = 0; for (var j = 0; j < 26; j++) { if (j == (W[i] - 'a')) break; if (C[j] > 0) count++; } C[W[i] - 'a'] = 0; rank += count * fact(W.length - i - 1); }
There is an explanation in https://en.wikipedia.org/wiki/Permutation#Numbering_permutations of how to convert a permutation on n objects to a number in the range 0..n!-1 and it goes on to say that "Converting successive natural numbers to the factorial number system produces those sequences in lexicographic order (as is the case with any mixed radix number system), and further converting them to permutations preserves the lexicographic ordering, provided the Lehmer code interpretation is used" So I would try doing this number conversion and see if it produces something related to the rank that you need, by your definition.
Extract keyphrases from text (1-4 word ngrams)
What's the best way to extract keyphrases from a block of text? I'm writing a tool to do keyword extraction: something like this. I've found a few libraries for Python and Perl to extract n-grams, but I'm writing this in Node so I need a JavaScript solution. If there aren't any existing JavaScript libraries, could someone explain how to do this so I can just write it myself?
I like the idea, so I've implemented it: See below (descriptive comments are included). Preview at: https://jsfiddle.net/WsKMx /*#author Rob W, created on 16-17 September 2011, on request for Stackoverflow (http://stackoverflow.com/q/7085454/938089) * Modified on 17 juli 2012, fixed IE bug by replacing [,] with [null] * This script will calculate words. For the simplicity and efficiency, * there's only one loop through a block of text. * A 100% accuracy requires much more computing power, which is usually unnecessary **/ var text = "A quick brown fox jumps over the lazy old bartender who said 'Hi!' as a response to the visitor who presumably assaulted the maid's brother, because he didn't pay his debts in time. In time in time does really mean in time. Too late is too early? Nonsense! 'Too late is too early' does not make any sense."; var atLeast = 2; // Show results with at least .. occurrences var numWords = 5; // Show statistics for one to .. words var ignoreCase = true; // Case-sensitivity var REallowedChars = /[^a-zA-Z'\-]+/g; // RE pattern to select valid characters. Invalid characters are replaced with a whitespace var i, j, k, textlen, len, s; // Prepare key hash var keys = [null]; //"keys[0] = null", a word boundary with length zero is empty var results = []; numWords++; //for human logic, we start counting at 1 instead of 0 for (i=1; i<=numWords; i++) { keys.push({}); } // Remove all irrelevant characters text = text.replace(REallowedChars, " ").replace(/^\s+/,"").replace(/\s+$/,""); // Create a hash if (ignoreCase) text = text.toLowerCase(); text = text.split(/\s+/); for (i=0, textlen=text.length; i<textlen; i++) { s = text[i]; keys[1][s] = (keys[1][s] || 0) + 1; for (j=2; j<=numWords; j++) { if(i+j <= textlen) { s += " " + text[i+j-1]; keys[j][s] = (keys[j][s] || 0) + 1; } else break; } } // Prepares results for advanced analysis for (var k=1; k<=numWords; k++) { results[k] = []; var key = keys[k]; for (var i in key) { if(key[i] >= atLeast) results[k].push({"word":i, "count":key[i]}); } } // Result parsing var outputHTML = []; // Buffer data. This data is used to create a table using `.innerHTML` var f_sortAscending = function(x,y) {return y.count - x.count;}; for (k=1; k<numWords; k++) { results[k].sort(f_sortAscending);//sorts results // Customize your output. For example: var words = results[k]; if (words.length) outputHTML.push('<td colSpan="3" class="num-words-header">'+k+' word'+(k==1?"":"s")+'</td>'); for (i=0,len=words.length; i<len; i++) { //Characters have been validated. No fear for XSS outputHTML.push("<td>" + words[i].word + "</td><td>" + words[i].count + "</td><td>" + Math.round(words[i].count/textlen*10000)/100 + "%</td>"); // textlen defined at the top // The relative occurence has a precision of 2 digits. } } outputHTML = '<table id="wordAnalysis"><thead><tr>' + '<td>Phrase</td><td>Count</td><td>Relativity</td></tr>' + '</thead><tbody><tr>' +outputHTML.join("</tr><tr>")+ "</tr></tbody></table>"; document.getElementById("RobW-sample").innerHTML = outputHTML; /* CSS: #wordAnalysis td{padding:1px 3px 1px 5px} .num-words-header{font-weight:bold;border-top:1px solid #000} HTML: <div id="#RobW-sample"></div> */
I do not know such a library in JavaScript but the logic is split text into array then sort and count alternatively split into array create a secondary array traversing each item of the 1st array check whether current item exists in secondary array if not exists push it as a item's key else increase value having a key = to item sought. HTH Ivo Stoykov
function ngrams(seq, n) { to_return = [] for (let i=0; i<seq.length-(n-1); i++) { let cur = [] for (let j=i; j<seq.length && j<=i+(n-1); j++) { cur.push(seq[j]) } to_return.push(cur.join('')) } return to_return } > ngrams(['a', 'b', 'c'], 2) ['ab', 'bc']