Javascript - Undefined arrays in iteration - javascript

I am working on a Nodejs tool which uses an API.
My issue is that I don't understand why the previous element of the array is not registered properly and is "Undefined" when the iterator is incremented.
this.pptab = new Array();
this.tabplayers = new Array();
for (var i = 0; i < (argsname.length - 1); i++) {
osuApi.getUser({ "u": argsname[i], "m": argsmode.join('=') }).then(user => { //calling the osu!api
this.pptab[i] = user[0].pp_raw;
this.tabplayers[i] = user[0].username;
console.log("Player -1: " + this.tabplayers[i - 1]); // This displays "Undefined" in each iteration
});
}

Two issue one in loop condition [ i **<** (argsname.length-1)] you do not require (-1 ) as of smaller then condition. Second if condition required for first step.
this.pptab = new Array();
this.tabplayers = new Array();
for (var i = 0; i < (argsname.length); i++) {
osuApi.getUser({ "u": argsname[i], "m": argsmode.join('=') }).then(user => { //calling the osu!api
this.pptab[i] = user[0].pp_raw;
this.tabplayers[i] = user[0].username;
if(i>0) {
console.log("Player -1: " + this.tabplayers[i-1]); // for first player -1 does not exist
}
});
}

I think you are doing something wrong when converting your string command to an array and then shifting it. See this:
Code is here:
https://repl.it/#LatinWarrior/StackoverflowQuestion
This is what I wrote:
let argsname = "!topsetup Player1 Player2 Player3 m=2".split(' ');
let argsnameWithShift = argsname.shift();
console.log(argsname);
console.log(argsnameWithShift);

Related

Javascript find the most repetitive character occurrence from the string

Let's say we have this string:
BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF
As you can see, here B is occurring 4 times at first but B is also present before DDDD.
Similarly, A is occurring 4 times at the beginning and later 6 times.
I want the expected output if I am searching B it should 4 times as the max occurrence B is 4. However if I am searching A then it should return 6 because the most occurrence for A is 6.
Here is my code I tried:
function checkRepeatativeString(str) {
let hashMap = {};
let seen = new Set();
let counter = 1;
let maxValue = 1;
let isPreviousValueSame = false;
let isNextValueSame = true;
for (let i = 0; i < str.length; i++) {
/**
* is previous value same
*/
if (str[i] == str[i-1]) {
isPreviousValueSame = true;
}
/**
* is next value same
*/
if (str[i] == str[i+1]) {
isNextValueSame = true;
}
if (seen.has(str[i]) && isPreviousValueSame) {
hashMap[str[i]][0]++;
hashMap[str[i]][1]++;
isPreviousValueSame = false;
} else if(seen.has(str[i]) && !isNextValueSame) {
maxValue = Math.max(hashMap[str[i]][1], maxValue);
counter = 0;
hashMap[str[i]] = [counter, maxValue];
} else {
maxValue = Math.max(maxValue, counter);
seen.add(str[i]);
hashMap[str[i]] = [counter, maxValue];
isPreviousValueSame = false;
}
}
return hashMap;
}
let str = "BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF";
console.log(checkRepeatativeString(str));
This code is working but if you look for B, I am getting stuck at the beginning of value.
My program returns out for B:
B: [ 1, 1 ]
^ ^
Inside array, 1 is a counter which scans the string and second 1 in array is a max value which should return the output. However my program is returning 1 for B. I am expecting 4 as max value.
Help would be appreciated~
Quick and dirty.
function maxConsecutiveCharacters(check, haystack) {
if(check.length !== 1) return false;
let result = 0;
let buffer = 0;
for(let i = 0; i < haystack.length; i++) {
if(haystack[i] === check) {
buffer++;
}
else {
if(buffer > result) {
result = buffer;
}
buffer = 0;
}
if(buffer > result) {
result = buffer;
}
}
return result;
}
That looks overly complicated. Consider approaching the problem from a different angle - first split up the string into segments of repeating characters, and group them into an object based on the length of the longest substring for a given character.
const checkRepeatativeString = (str) => {
const longestCounts = {};
for (const consecutive of (str.match(/(.)\1*/g) || [])) {
const char = consecutive[0];
longestCounts[char] = Math.max(
longestCounts[char] || 0, // Use the existing value in the object if it exists and is higher
consecutive.length // Otherwise, use the length of the string iterated over
);
}
return longestCounts;
};
let str = "BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF";
console.log(checkRepeatativeString(str));
Simpler code often means less surface area for bugs.

Compare an array with itself for duplicate and store in new array

I have a question, how do I compare values ​​in an array with themselves without the value to be compared being shown as true?
As an example, if the value EUW1_6011808396 occurs again, it should be stored in the array matchingMatches.
The Values of the Array are:
[EUW1_6011808396, EUW1_6011824351, EUW1_6011720277, EUW1_6010413995, EUW1_6010218048, EUW1_6010184913, EUW1_6010131700, EUW1_6009739853, EUW1_6008825456, EUW1_6008833322, EUW1_6008409245, EUW1_6008369887, EUW1_6008355242, EUW1_6007567238, EUW1_6007269146, EUW1_6007226284, EUW1_6007192332, EUW1_6005571988, EUW1_6005438941, EUW1_6005495312, EUW1_6013263286, EUW1_6013193252, EUW1_6012475324, EUW1_6012411610, EUW1_6012315128, EUW1_6012011561, EUW1_6011110477, EUW1_6011026046, EUW1_6009739853, EUW1_6006439870, EUW1_6006434580, EUW1_6005238786, EUW1_6005191249, EUW1_6005026992, EUW1_6005015187, EUW1_6004958241, EUW1_6003811368, EUW1_6002847479, EUW1_6002164371, EUW1_6002148723, EUW1_6015524685, EUW1_6015387328, EUW1_6015402003, EUW1_6014779337, EUW1_6014724668, EUW1_6014701498, EUW1_6014655368, EUW1_6014580839, EUW1_6014429620, EUW1_6014475971, EUW1_6014473252, EUW1_6013334881, EUW1_6013322375, EUW1_6012669749, EUW1_6012635347, EUW1_6012583396, EUW1_6010971941, EUW1_6006896961, EUW1_6006881165, EUW1_6006518887, EUW1_6015745842, EUW1_6015589872, EUW1_6014068520, EUW1_6014044304, EUW1_6007955310, EUW1_6003705297, EUW1_6003569783, EUW1_6002003834, EUW1_6000787500, EUW1_5994465297, EUW1_5993391050, EUW1_5992233473, EUW1_5992169601, EUW1_5984062877, EUW1_5984034743, EUW1_5983855739, EUW1_5983880569, EUW1_5983766086, EUW1_5982931745, EUW1_5982372929, EUW1_6005238786, EUW1_6005191249, EUW1_6005026992, EUW1_6005015187, EUW1_6004958241, EUW1_6002164371, EUW1_6002148723, EUW1_6002057259, EUW1_6002053660, EUW1_6002009239, EUW1_6002003834, EUW1_6001939719, EUW1_6001867883, EUW1_6001022392, EUW1_6000887143, EUW1_6000892356, EUW1_6000787500, EUW1_6000820954, EUW1_5996485374, EUW1_5994559073, EUW1_6010083174, EUW1_6010017420, EUW1_6006898776, EUW1_6006838293, EUW1_6005225782, EUW1_6005135031, EUW1_6003899867, EUW1_6003883079, EUW1_6003786523, EUW1_6002164371, EUW1_6002148723, EUW1_6002057259, EUW1_6002053660, EUW1_6002009239, EUW1_6001022392, EUW1_6000887143, EUW1_6000892356, EUW1_6000787500, EUW1_5999368247, EUW1_5999295110, EUW1_5989231240, EUW1_5989055249, EUW1_5987149834, EUW1_5978125118, EUW1_5969701977, EUW1_5969187233, EUW1_5956294382, EUW1_5955846040, EUW1_5949708234, EUW1_5934525960, EUW1_5916275391, EUW1_5916168691, EUW1_5916132470, EUW1_5907690529, EUW1_5897979620, EUW1_5897921186, EUW1_5896786548, EUW1_5880625543, EUW1_5880541891, EUW1_5878457213, EUW1_6015589872, EUW1_5969852994, EUW1_5969788713, EUW1_5940681289, EUW1_5940556247, EUW1_5937866203, EUW1_5937892773, EUW1_5937768916, EUW1_5937765393, EUW1_5934600651, EUW1_5934512860, EUW1_5934339533, EUW1_5932238516, EUW1_5932166008, EUW1_5932211862, EUW1_5929934534, EUW1_5930153889, EUW1_5931242804, EUW1_5919621815, EUW1_5918236611]
The function which is used for getting the Id for the API call.
/**
* Getting the Team Members and their IDs
*
* #param {info_id|String}
* #return Team Members IDs
* #customfunction
*/
function getTeamMembersIDs(info_id) {
try {
startup();
var teamMembers = getTeamMembersSheet();
var riotIds = [];
for (var counter = 0; counter != teamMembers.length; counter = counter + 1) {
switch (info_id){
case "puuid":
var data = buildURL("https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/"+teamMembers[counter]+"?api_key="+apiKey);
var puuid = data["puuid"];
riotIds.push(puuid);
break;
case "id":
var data = buildURL("https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/"+teamMembers[counter]+"?api_key="+apiKey);
var id = data["id"];
riotIDs.push(id);
break;
case "accountId":
var data = buildURL("https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/"+teamMembers[counter]+"?api_key="+apiKey);
var accountId = data["accountId"];
riotIds.push(accountId);
break;
}
}
return riotIds
} catch (err) {
Logger.log('Failed with error %s', err.message);
}
}
This Function is Used for Getting the Matches that got played per Team Member.
/**
* Getting the Data from the Api which matches got played
*
* #return the matches played
* #customfunction
*/
function getMatchesPlayed() {
try{
startup();
var puuids = getTeamMembersIDs("puuid");
var games = [];
for (var counter = 0; counter != puuids.length; counter = counter + 1) {
var data = buildURL("https://europe.api.riotgames.com/lol/match/v5/matches/by-puuid/"+puuids[counter]+"/ids?start=0&count=20&api_key="+apiKey)
for(var counter2 = 0; counter2 != data.length; counter2 = counter2 + 1){
games.push(data[counter2])
}
}
return games
}catch (err) {
Logger.log('Failed with error %s', err.message);
}
}
The function which should return the Matching Matches in the Array gets from the getMatchesPlayed function.
/**
* Checking the games for matches
*
* #return the matches which match
* #customfunction
*/
function getMatchingMatches() {
try{
var games = getMatchesPlayed();
var matchingMatches = [];
Logger.log(games)
const gettingGames = games.map(element => element)
for (var counter = 0; counter != games.length; counter = counter + 1){
Logger.log(gettingGames[counter])
}
}catch (err) {
Logger.log('Failed with error %s', err.message);
}
}
As I understand, you need to get duplicate ids.
Use the Set data structure for comparing the array values.
// assume this should return an array [EUW1_6011808396, EUW1_6011824351]
var games = getMatchesPlayed(); //
var matched_games = []
// Initialise set data structure.
var set = = new Set();
//Iterate over the games. Runtime complexity O(n)
games.forEach((element) => {
//the case when set does not contain the id we should add it to `set`
if(!set.has(element)){ // O(1)
set.add(element); // O(1)
continue;
}
// if the set already contains the id, we should add it to the matching games array.
matched_games.push(element);
})
I hope this will help.
welcome to Stackoverflow! You have a lot of ways to store duplicate values In a new array, and this is one.
// get duplicartes and store them in a array
function getDuplicates(array) {
let sorted = array.sort();
for (let i = 0; i < sorted.length - 1; i++) {
if (sorted[i + 1] == sorted[i]) {
duplicates.push(sorted[i]);
}
}
}
Another way to get non-unique values
const euws = ['EUW1_6011808396','EUW1_6011824351','EUW1_6011720277','EUW1_6010413995','EUW1_6010218048','EUW1_6010184913','EUW1_6010131700','EUW1_6009739853','EUW1_6008825456','EUW1_6008833322','EUW1_6008409245','EUW1_6008369887','EUW1_6008355242','EUW1_6007567238','EUW1_6007269146','EUW1_6007226284','EUW1_6007192332','EUW1_6005571988','EUW1_6005438941','EUW1_6005495312','EUW1_6013263286','EUW1_6013193252','EUW1_6012475324','EUW1_6012411610','EUW1_6012315128','EUW1_6012011561','EUW1_6011110477','EUW1_6011026046','EUW1_6009739853','EUW1_6006439870','EUW1_6006434580','EUW1_6005238786','EUW1_6005191249','EUW1_6005026992','EUW1_6005015187','EUW1_6004958241','EUW1_6003811368','EUW1_6002847479','EUW1_6002164371','EUW1_6002148723','EUW1_6015524685','EUW1_6015387328','EUW1_6015402003','EUW1_6014779337','EUW1_6014724668','EUW1_6014701498','EUW1_6014655368','EUW1_6014580839','EUW1_6014429620','EUW1_6014475971','EUW1_6014473252','EUW1_6013334881','EUW1_6013322375','EUW1_6012669749','EUW1_6012635347','EUW1_6012583396','EUW1_6010971941','EUW1_6006896961','EUW1_6006881165','EUW1_6006518887','EUW1_6015745842','EUW1_6015589872','EUW1_6014068520','EUW1_6014044304','EUW1_6007955310','EUW1_6003705297','EUW1_6003569783','EUW1_6002003834','EUW1_6000787500','EUW1_5994465297','EUW1_5993391050','EUW1_5992233473','EUW1_5992169601','EUW1_5984062877','EUW1_5984034743','EUW1_5983855739','EUW1_5983880569','EUW1_5983766086','EUW1_5982931745','EUW1_5982372929','EUW1_6005238786','EUW1_6005191249','EUW1_6005026992','EUW1_6005015187','EUW1_6004958241','EUW1_6002164371','EUW1_6002148723','EUW1_6002057259','EUW1_6002053660','EUW1_6002009239','EUW1_6002003834','EUW1_6001939719','EUW1_6001867883','EUW1_6001022392','EUW1_6000887143','EUW1_6000892356','EUW1_6000787500','EUW1_6000820954','EUW1_5996485374','EUW1_5994559073','EUW1_6010083174','EUW1_6010017420','EUW1_6006898776','EUW1_6006838293','EUW1_6005225782','EUW1_6005135031','EUW1_6003899867','EUW1_6003883079','EUW1_6003786523','EUW1_6002164371','EUW1_6002148723','EUW1_6002057259','EUW1_6002053660','EUW1_6002009239','EUW1_6001022392','EUW1_6000887143','EUW1_6000892356','EUW1_6000787500','EUW1_5999368247','EUW1_5999295110','EUW1_5989231240','EUW1_5989055249','EUW1_5987149834','EUW1_5978125118','EUW1_5969701977','EUW1_5969187233','EUW1_5956294382','EUW1_5955846040','EUW1_5949708234','EUW1_5934525960','EUW1_5916275391','EUW1_5916168691','EUW1_5916132470','EUW1_5907690529','EUW1_5897979620','EUW1_5897921186','EUW1_5896786548','EUW1_5880625543','EUW1_5880541891','EUW1_5878457213','EUW1_6015589872','EUW1_5969852994','EUW1_5969788713','EUW1_5940681289','EUW1_5940556247','EUW1_5937866203','EUW1_5937892773','EUW1_5937768916','EUW1_5937765393','EUW1_5934600651','EUW1_5934512860','EUW1_5934339533','EUW1_5932238516','EUW1_5932166008','EUW1_5932211862','EUW1_5929934534','EUW1_5930153889','EUW1_5931242804','EUW1_5919621815','EUW1_5918236611'];
const nonUnique = euws.reduce((r, v, i, a) => {
if (!r.includes(v) && a.indexOf(v) !== i) r.push(v);
return r;
}, []);
console.log(nonUnique)
.as-console-wrapper { max-height: 100% !important; top: 0 }

How to set a variable once a day in Javascript

Coniser a variable list = ["A", "B",...] as list of strings. I want to use a Javascript programm that picks three strings from this list once a day and writes it into a HTML field.
Currently I use
function getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
smallList = getRandom(list, 3);
var htmlTags = [
"tag1",
"tag2",
"tag3"
];
for (var i = 0; i < htmlTags.length; i++) {
document.getElementById(htmlTags[i]).innerHTML = smallList[i];
}
Now this list gets new entries every time I refresh the website. Is there a way that smallList is only set once a day/hour/min/ in a pedriod of time only using javascript?
So you want to:
Pick three values from your list and show them on your web page
Each day, pick three new values to show for the whole day
Everyone who visits the page should see the same values regardless of client
As others have suggested, it would be a better candidate for a server-side task than client-side.
For example, you might have a server page which checks for the existence of a value stored in cache. The cache would be set to 24 hours. If the cache is not available, then a new cache object is created and given a half-life of 24 hours. Inside the cache, you could also store the values you wish to retrieve.
Then, you could retrieve the cache and output the values. The particular implementation of the cache would depend on your server-side language.
OKAY: Via Stored Values (COOKIE, SESSION, LOCAL STORAGE, MEMORY):
per user you'd have to use a cookie, session, or write to local storage in the browser.
for all users you'd have to use a server variable somewhere like a database, file, or memory.
you'd set the value to expire in a day and regenerate if expired. this is the answer you will get from most people because it is the only way they know how to solve this, single set value polled from all locations.
BETTER: Via Deterministic Pseudo-Random Number Generator Seeded with Date:
or if you are really ambitious and don't want to rely on a value that you set somewhere, you could use or write a:
deterministic pseudo-random number generator that you seed off of the date. Since deterministic pseudo-random generators produce reproducible "randoms" from the same seed, seeding the date gives you a unique seed per day, hence a unique random per day.
function RC4(seed) {
this.s = new Array(256);
this.i = 0;
this.j = 0;
for (var i = 0; i < 256; i++) {
this.s[i] = i;
}
if (seed) {
this.mix(seed);
}
};
RC4.getStringBytes = function(string) {
var output = [];
for (var i = 0; i < string.length; i++) {
var c = string.charCodeAt(i);
var bytes = [];
do {
bytes.push(c & 0xFF);
c = c >> 8;
} while (c > 0);
output = output.concat(bytes.reverse());
}
return output;
};
RC4.prototype._swap = function(i, j) {
var tmp = this.s[i];
this.s[i] = this.s[j];
this.s[j] = tmp;
};
RC4.prototype.mix = function(seed) {
var input = RC4.getStringBytes(seed);
var j = 0;
for (var i = 0; i < this.s.length; i++) {
j += this.s[i] + input[i % input.length];
j %= 256;
this._swap(i, j);
}
};
RC4.prototype.next = function() {
this.i = (this.i + 1) % 256;
this.j = (this.j + this.s[this.i]) % 256;
this._swap(this.i, this.j);
return this.s[(this.s[this.i] + this.s[this.j]) % 256];
};
function RNG(seed) {
if (seed == null) {
seed = '' + Math.random() + Date.now();
} else if (typeof seed === "function") {
// Use it as a uniform number generator
this.uniform = seed;
this.nextByte = function() {
return ~~(this.uniform() * 256);
};
seed = null;
} else if (Object.prototype.toString.call(seed) !== "[object String]") {
seed = JSON.stringify(seed);
}
this._normal = null;
if (seed) {
this._state = new RC4(seed);
} else {
this._state = null;
}
}
RNG.prototype.nextByte = function() {
return this._state.next();
};
RNG.prototype.uniform = function() {
var BYTES = 7; // 56 bits to make a 53-bit double
var output = 0;
for (var i = 0; i < BYTES; i++) {
output *= 256;
output += this.nextByte();
}
return output / (Math.pow(2, BYTES * 8) - 1);
};
RNG.prototype.random = function(n, m) {
if (n == null) {
return this.uniform();
} else if (m == null) {
m = n;
n = 0;
}
return n + Math.floor(this.uniform() * (m - n));
};
RNG.$ = new RNG();
Date.prototype.yyyymmdd = function() {
var mm = this.getMonth() + 1; // getMonth() is zero-based
var dd = this.getDate();
return [this.getFullYear(), !mm[1] && '0', mm, !dd[1] && '0', dd].join(''); // padding
};
// Using the Date like so will give you the same random between 40 and 50 for the same day
var rng = new RNG((new Date).yyyymmdd()); rng.random(40, 50);
// Test with dates
var rng = new RNG('20180301'); rng.random(40, 50);
var rng = new RNG('20180302'); rng.random(40, 50);
var rng = new RNG('20180301'); rng.random(40, 50);
Store the list in localStorage or a Cookie. Also store the timestamp.
Use setTimeout(function(){...}, n) to examine the timestamp and update the values as needed.
If the page refreshes or is loaded anew, then perform the check on what is stored. If nothing exists, create your list and set the timestamp. If data does exist, then compare the timestamp and update the list as needed.
If you need the list to be consistent across users, then everything needs to be stored, examined and calculated on the server-side.
localStorage.savedData = {
timestamp: new Date(),
dataList: ['a','b','c']
}
To get the values from localStorage:
// you don't have to create variables, you can just use localStorage.[property] to get compare any value
let ts = localStorage.timestamp; // Date object
let dl = localStorage.dataList; // Array of values
For more information on localStorage see (or search the web) -> https://www.w3schools.com/html/html5_webstorage.asp

NodeJS require with asynch functions when synch is wanted

I have the following code
var utils = require(`${__dirname}/../../utils/utils.js`);
...
let object = utils.parse(input);
if (object === undefined){
let helper = utils.recognize(input);
msg.channel.sendMessage("\"" + input + "\" not recognized. Did you mean \"" + helper[0] + "\"?");
object = utils.parse(helper[0]);
}
//code related to object
console.log(object.strLength);
where "parse" tries to match the input to an object in a database, and "recognize" tries to find the best match if the input is spelled incorrectly (Levenshtein) (along with additional info such as how close the match was).
Currently the issue is that the code is ran asynchronously; "object.strLength" returns an undefined before utils.recognize() returns a value. If I copy/paste the recognize() and parse() functions into the file, then the code is run synchronously and I do not run into any issues. However I would rather keep those functions in a separate file as I reuse them in other files.
Is there a way to specify that the functions in utils must be synch? I know that there are libraries that convert asynch into synch but I prefer to use as few libraries as I can help it. I tried to have the recognize functions return a Promise but it ended up as a jumbled mess
edit: here's parse. I did not think it was necessary to answer this question so I did not include it initially:
var db = require(`${__dirname}/../data/database.js`);
...
var parse = (input) => {
let output = db[output];
if (output === null) {
Object.keys(db).forEach((item) => {
if (db[item].num === parseInt(input) || (db[item].color + db[item].type === input)){
output = db[item];
return false;
}
});
}
return output;
}
I solved the issue, thanks everyone. Here's what was wrong, it was with recognize(). It was my mistake to not show the code for it initially.
Original recognize:
var recognize = (item) => {
//iterate through our databases and get a best fit
let bestItem = null;
let bestScore = 99999; //arbitrary large number
//let bestType = null;
//found algorithm online by milot-mirdita
var levenshtein = function(a, b) {
if (a.length == 0) { return b.length; }
if (b.length == 0) { return a.length; }
// swap to save some memory O(min(a,b)) instead of O(a)
if(a.length > b.length) {
let tmp = a;
a = b;
b = tmp;
}
let row = [];
for(let i = 0; i <= a.length; i++) {
row[i] = i;
}
for (let i = 1; i <= b.length; i++) {
let prev = i;
for (let j = 1; j <= a.length; j++) {
let val;
if (b.charAt(i-1) == a.charAt(j-1)) {
val = row[j-1]; // match
} else {
val = Math.min(row[j-1] + 1, // substitution
prev + 1, // insertion
row[j] + 1); // deletion
}
row[j - 1] = prev;
prev = val;
}
row[a.length] = prev;
}
return row[a.length];
}
//putting this here would make the code work
//console.log("hi");
Object.keys(db).forEach((key) => {
if (levenshtein(item, key) < bestScore) {
bestItem = key;
bestScore = levenshtein(item, key);
}
});
return [bestItem, bestScore];
}
My solution was to move the levenshtein function outside of the recognize function, so if I wanted to I can call levenshtein from another function
#user949300 and #Robert Moskal, I changed the forEach loop into a let...in loop. There is no functional difference (as far as I can tell) but the code does look cleaner.
#Thomas, I fixed the let output = db[output]; issue, oops.
Again, thanks for all of your help, I appreciate it. And happy New Year too

Can't iterate array returned from Sizzle

From my understanding Sizzle returns an array of objects (DOMElements), I am trying to walk that array of objects in a for loop but I am getting errors. When I try to get a property with the
obj[index-number]["property"]
it works fine, but when I try to access it after passing it to another function
obj[index-number][arguments[index-number]]
I am getting a return of undefined. I have tried many different ways, including eval to parse the dot notation to no avail. I am stumped. Any pointers or ideas would be awesome. Also, I have verified all input to the function is correct (through alerting them out), also, hard coding the values to get what I want in the function works as well. Here is my code: (sorry it's lengthy).....
var ecmafw = function() {
// Creates the new instance of the object.
// Sets up the objects global properties:
this.error = false;
// Checks to see if arguments were supplied, if none are then it returns false.
if (arguments.lenght == 0) {
this.error = "No arguments were supplied.";
return false;
}
// Gives a reference to the result set.
this.results = Sizzle(arguments[0]);
this.attr = function() {
/* Purpose: To add/remove/update an attribute from the results set.
*
* Can be used in two ways:
* 1: .attr("attribute1='value' attribute2='value' attribute3='value'") // adds/removes them all. [negate value to be removed with "-" (used for class)]
* 2: .attr("attribute", "value") // adds the one. [negate value to be removed with "-" (used for class)]
* 3: .attr("attribute") // removes the one.
* 4: .attr("attribute1 attribute2 attribute3") // removes them all.
*/
var len = this.results.length;
switch (arguments.length) {
case 1:
for (var a=0; a < len; a++) {
var re = new RegExp("=", "g");
if (re.test(arguments[0])) {
// Provided a list of attributes to update/create.
valuePairs = arguments[0].split("' ");
for (var i=0; i < valuePairs.length; i++) {
var attributeValue = valuePairs[i].split("=");
var newRE = new RegExp(/^-/);
var value = attributeValue[1].replace(/'/g, "");
if (newRE.test(value)) {
this.removeAttr(attributeValue[0], a, value);
} else {
this.setAttr(attributeValue[0], value, a);
}
}
} else {
var attributeSplit = arguments[0].split(" ");
if (attributeSplit.length == 1) {
// Provided a single attributes to remove.
this.removeAttr(arguments[0], a);
} else {
// Provided multiple attributes to remove.
for (var i=0; i < attributeSplit.length; i++) {
this.removeAttr(attributeSplit[i], a);
}
}
}
}
break;
case 2:
// Provided a single name/value pair to update.
for (var a=0; a < len; a++) {
this.setAttr(arguments[0], arguments[1], a)
}
break;
default:
// Either 0 or more than 2 arguments were supplied.
this.error = "There were no arguments supplied with the attr() function, or there were too many supplied.";
return false
break;
}
};
this.setAttr = function() {
// Counters for IE className
if (document.all && !window.opera) {
arguments[0] = arguments[0].replace(/class/gi, "className");
}
if (arguments[0] == "class" || arguments[0] == "className") {
if (this.results[arguments[2]][arguments[0]] != undefined) {
arguments[1] += " " + this.results[arguments[2]][arguments[0]]; // Failing
}
}
if (this.results[arguments[2]].setAttribute) {
this.results[arguments[2]].setAttribute(arguments[0], arguments[1]);
} else {
this.results[arguments[2]][arguments[0]] = arguments[1];
}
};
this.removeAttr = function() {
arguments[0] = arguments[0].replace(/class/gi, "className");
var item = this.results[arguments[1]];
if (arguments[0] == "className") {
arguments[2] = arguments[2].replace("-", "");
var replaceRE = new RegExp(arguments[2], "gi");
// For some reason it would find it like this, This is fine but it is not working
// in Opera. Opera is failing to convert item[eachItem] to an object. (so it says in its error log)
for (var eachItem in item) {
if (arguments[0] == eachItem) {
item[eachItem] = item[eachItem].replace(replaceRE, " ");
item[eachItem] = item[eachItem].replace(/ /gi, " ");
item[eachItem] = item[eachItem].replace(/^ /gi, "");
item[eachItem] = item[eachItem].replace(/ $/gi, "");
}
}
} else {
if (this.results[arguments[1]].removeAttribute) {
this.results[arguments[1]].removeAttribute(arguments[0]);
} else {
this.results[arguments[1]][arguments[0]] = "";
}
}
};
// Returns a reference to itself.
return this;
}
Not sure if this might be the problem, but in the removeAttr function you are accessing the 3rd argument passed in on this line:
arguments[2] = arguments[2].replace("-", "");
However, in 2 of the 3 calls to this function you only pass in 2 arguments. If the above line runs in either of those cases arguments[2] would be undefined and calling replace("-", "") on it would throw an error.
Also, you have a typo in your initial arguments check near the top: arguments.lenght.

Categories