So recently I built a search and replace program with Java, now I am working on translating/rebuilding that program with JavaScript. However, I am having trouble finding JS method alternatives for next() and hasNext(). I am new to JS so I don't know what JS methods would work similarly to the Java methods I am used to.
This is my program, I commented through it to show exactly what I am doing with the previously mentioned methods. Basic set up, 2 text areas, one for the search box (search criteria, box 2), and one for the main document (the field of search, box 1). It basically boils down to a cross-reference. It will highlight all the similarities between the documents.
function search() {
//define an array to store the search criteria.
var array = [];
// define a counter.
var n = 0;
// define a constant for the first box, the search field.
const box1 = document.getElementById("box1");
// define a constant for the second box, the search criteria.
const box2 = document.getElementById("box2");
// loop through the search criteria, storing each word as a seperate element in the array.
// this uses non js terms, this is where I need the help.
while (box2.hasNext()) {
array[n] = box2.next();
n = n + 1;
}
// resets the counter.
n = 0;
// loops through each search item, finding and replacing each item with itself, surrounded by mark tags.
while (n <= array.length) {
box1.replace(array[n], "<mark>" + array[n] + "</mark>");
}
}
</script>
There is bound to be other issues, bugs and syntax, feel free to point them out but lets try and keep the focus on the methodology (i.e. method alternatives for next() and hasNext()).
Thanks.
-EDIT- I'd prefer to use native alternative (no jquery) becuase I know even less about jquery than I do js.
Related
I was wondering if anyone could help me solve this issue or point me towards the right direction.
In my project we have a filed that needs to be autofilled, at this moment I use onblur which works wonders as it only does it so once you leave the focus. However, due to recent changes, it needs to only do so when there is only one unique item in the map which it matches the input.
I have a large array defined as following:
var myArray = [
[content, content],
[content, content],
...
]
Later in my code I associate it with a map, at least this is what most stackoverflow questions I looked at referred to it as follows:
var myMap = {};
for(0 to myArray.length) {
var a = myArray[i][0];
var b = myArray[i][1];
myMap[a] = b;
}
Now, finally I iterate over this array as follows:
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0)
the above is the line of code I am struggling to figure out how to change. At this moment, while using on blur, if I type in the letter 'A' for example, and leave the focus area it will automatically fill it in with a certain name. However, in the array there are many other objects that begin with, or contain A. How can I change it so that the onkeydown event will keep going until it finally filters it down to to only possible key-value pair? I tried looking at MDN's documentation for filtering, but I do not think that will work for my purposes, or at least I am too inexperienced with JS.
If the indexOf the first and last are nonnegative and equal, there is just one. You could do this with an && and boolean short circuit evaluation, but that will run very far right off the screen, so I am showing your code with one more nested if (up to you to add the end of the block). But we also need to see if there are matches on multiple keys.
var matchCount=0;
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0){
if (map[key].toLowerCase().indexOf(location.toLowerCase()) == map[key].toLowerCase().lastIndexOf(location.toLowerCase())) {
matchCount++;
then outside your for loop:
if (matchCount==1){ //do your stuff
I have a group of strings in Javascript and I need to write a function that detects if another specific string belongs to this group or not.
What is the fastest way to achieve this? Is it alright to put the group of values into an array, and then write a function that searches through the array?
I think if I keep the values sorted and do a binary search, it should work fast enough. Or is there some other smart way of doing this, which can work faster?
Use a hash table, and do this:
// Initialise the set
mySet = {};
// Add to the set
mySet["some string value"] = true;
...
// Test if a value is in the set:
if (testValue in mySet) {
alert(testValue + " is in the set");
} else {
alert(testValue + " is not in the set");
}
You can use an object like so:
// prepare a mock-up object
setOfValues = {};
for (var i = 0; i < 100; i++)
setOfValues["example value " + i] = true;
// check for existence
if (setOfValues["example value 99"]); // true
if (setOfValues["example value 101"]); // undefined, essentially: false
This takes advantage of the fact that objects are implemented as associative arrays. How fast that is depends on your data and the JavaScript engine implementation, but you can do some performance testing easily to compare against other variants of doing it.
If a value can occur more than once in your set and the "how often" is important to you, you can also use an incrementing number in place of the boolean I used for my example.
A comment to the above mentioned hash solutions.
Actually the {} creates an object (also mentioned above) which can lead to some side-effects.
One of them is that your "hash" is already pre-populated with the default object methods.
So "toString" in setOfValues will be true (at least in Firefox).
You can prepend another character e.g. "." to your strings to work around this problem or use the Hash object provided by the "prototype" library.
Stumbled across this and realized the answers are out of date. In this day and age, you should not be implementing sets using hashtables except in corner cases. You should use sets.
For example:
> let set = new Set();
> set.add('red')
> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false
Refer to this SO post for more examples and discussion: Ways to create a Set in JavaScript?
A possible way, particularly efficient if the set is immutable, but is still usable with a variable set:
var haystack = "monday tuesday wednesday thursday friday saturday sunday";
var needle = "Friday";
if (haystack.indexOf(needle.toLowerCase()) >= 0) alert("Found!");
Of course, you might need to change the separator depending on the strings you have to put there...
A more robust variant can include bounds to ensure neither "day wed" nor "day" can match positively:
var haystack = "!monday!tuesday!wednesday!thursday!friday!saturday!sunday!";
var needle = "Friday";
if (haystack.indexOf('!' + needle.toLowerCase() + '!') >= 0) alert("Found!");
Might be not needed if the input is sure (eg. out of database, etc.).
I used that in a Greasemonkey script, with the advantage of using the haystack directly out of GM's storage.
Using a hash table might be a quicker option.
Whatever option you go for its definitely worth testing out its performance against the alternatives you consider.
Depends on how much values there are.
If there are a few values (less than 10 to 50), searching through the array may be ok. A hash table might be overkill.
If you have lots of values, a hash table is the best option. It requires less work than sorting the values and doing a binary search.
I know it is an old post. But to detect if a value is in a set of values we can manipulate through array indexOf() which searches and detects the present of the value
var myString="this is my large string set";
var myStr=myString.split(' ');
console.log('myStr contains "my" = '+ (myStr.indexOf('my')>=0));
console.log('myStr contains "your" = '+ (myStr.indexOf('your')>=0));
console.log('integer example : [1, 2, 5, 3] contains 5 = '+ ([1, 2, 5, 3].indexOf(5)>=0));
You can use ES6 includes.
var string = "The quick brown fox jumps over the lazy dog.",
substring = "lazy dog";
console.log(string.includes(substring));
The description of Javascript function parameters on W3Schools wasn't very clear, so I just want to clarify.
From my understanding, there isn't a type restriction; the "real value" of the parameters are passed into the method. Is there a way to pass objects or elements? Or is that what is meant by "real value"?
For example:
The function displayText meant to take input text and set a display to show a new word in the given input text, going to the next word every time it's called.
function displayText() {
var text = document.getElementById("words").value;
// Since text is initialized
// every time the method is called,
// it will always start at the beginning of the text area.
// Not sure how to fix this since making `text`
// a global variable doesn't work
var list = text.split(/[ \t\n]+/);
displayNext(list, "display");
}
There is a "helper" method, displayNext, which is supposed to shift to the next word in the list and sets the display to that word.
function displayNext(list, textboxID) {
document.getElementById(textboxID).innerHTML = list.shift();
}
This isn't working as it is intended. I'm fairly sure it's because I've mucked up something with the parameters, since displayNext sets innerHTML to null. list must have not passed properly. I'm not sure how to fix this.
I'm sure there's a more efficient way to do this, but this is a good opportunity to learn how Javascript parameters actually work, so I thought I'd ask.
JSFiddle
Based on the comments in your code, it sounds like you want displayText() to display the "next" word each time. To do that, you have to create some place to store some state about which word is the next one to display. As you have it now, you create a new array every time and always display the first word.
The simplest way is to create a variable outside your function in some lasting scope where you store the word number:
var wordNum = 0;
function displayText() {
var text = document.getElementById("words").value;
var list = text.split(/\s+/);
if (list.length !== 0) {
// make sure we aren't off the end of the list
wordNum = wordNum % list.length;
displayNext(list[wordNum++], "display");
}
}
function displayNext(text, textboxID) {
document.getElementById(textboxID).innerHTML = text;
}
For a lot more info about arguments to Javascript functions and even how you can detect what arguments were passed or overload arguments, see this answer: How to overload functions in javascript? and for more info about how arguments are passed: Javascript by reference vs. by value
so I've been on here for awhile, and I'm still considered an entry level programmer based on my general knowledge of structure and basic concepts. I have a function below that was given to me in an answer for a different question I asked. I can understand most of what it is doing, but I need help understanding the rest of what it does. I'm asking this because I would really like to understand further advanced concepts of javascript, and jQuery.
So what I've done below is placed the function, and I'll comment in what I know about what the function is doing at where, and then I'll place question marks where I"m confused.
function validate(){
//array of objeccts used to defined the class selector for each element iterated
//with what validation function is be assigned to that specific selector
var fields = [
{
selector: $('.not-empty'),
validations: [ isNotEmpty]
},
{
selector: $('.email'),
validations: [ isNotEmpty, isEmail]
},
{
selector: $('.number'),
validations: [ isNotEmpty, isNumber]
},
{
selector: $('.number-noreq'),
validations: [isNumberNotRequired]
},
{
selector: $('.checked'),
validations: [isChecked]
}
];
//remove any classes of 'has-error' from each element traversed before validation begins
$('.form-control').closest('.form-group').removeClass('has-error');
//defining variables
var i = 0, k = 0, z = 0, j = fields.length, item, selector, fn, info;
//for loop to traverse the fields array of objects
for(; i < j; i++){
item = fields[i];
//traversing each field.validation
for(k = 0; k < item.validations.length; k++){
fn = item.validations[k]; //setting fn as a function found in validation
//traversing each selector in item
for( z = 0; z < item.selector.length; z++){
selector = $(item.selector[z]); //setting the selector
//attempting to set info to the closest form or input group found by the selector
info = selector.closest('.form-group, .input-group');
if(info) //if info contains data
//?????????????????????????????????????? no idea what's going on below other
//other than it's running the validation function that was passed, but why
//is it written like this and what is it doing?
info[fn(selector.val()) ? 'removeClass' : 'addClass']('has-error');
}
}
}
}
So that is the basic question I have for this code (where all the question marks are). If someone can clearly answer what is going on, why you write the code like that, what the purpose of it is, and is it benefcial or not, would be fantastic. if you need more clarification I would be happy to provide it. I just want to be able to explain the code to somebody and know what I am talking about instead of trying to have to bs my through it. I think it was Einstein who said, "If you can't explain something accurately and to the point, then you truly do not understand it" or something like that!
Thank you in advance!
EDIT: here are the functions that 'validations' traverse through
//validation functions
function isNotEmpty(value){
return value && $.trim(value).length > 0;
}
function isEmail(value){
return /^([^#\s\t\n]+\#[\w\d]+\.[\w]{2,3}(\.[\w]{2})?)$/.test(value);
}
function isNumber(value){
return /^\d+$/.test(value);
}
function isNumberNotRequired(value){
return /^\d+$/.test(value) || value.length < 1;
}
function isChecked(value){
var r = false;
var name = $(value).attr('name');
$('input[name="'+name+'"').each(function(){
if($(this).is(':checked')){
r = true;
}
});
return r;
}
SECOND EDIT/UPDATE: We have determined that there is a severe error in the code that allows it not to keep track of the validation and take into account previous validations for input groups, and other related sections. How does this corrected. I'm testing items on jsfiddle at the moment I will return when I have restuls!
This line:
info[fn(selector.val()) ? 'removeClass' : 'addClass']('has-error');
is equivalent to this:
var result = fn(selector.val());
if (result)
info.removeClass("has-error");
else
info.addClass("has-error");
How is that? Well, your code calls the function plucked from the list of validation routines stored in that data structure, passing the value of the field to be tested. The result of that function call is used as a true/false test in the ? : expression. If the result is true, the ? : resolves to the string "removeClass"; if false, to "addClass".
Now, what is info? It's a jQuery object that refers to the closest piece of the DOM that (presumably) is where an error message would be displayed, or where some other indicator would be shown based on some CSS rule. The [ ] operator will take whichever of those two strings the ? : resolves to and use that as a property accessor. The net effect, therefore, is to reference either info.removeClass or info.addClass. Those are both references to jQuery methods, so one or the other will be called. In either case, the code wants to operate on the class name "has-error", because it wants to either add it (when the validation fails) or remove it (when the validation succeeds).
That said, the code has a serious defect: if, for a given field, there is in fact a list of validation functions, the code will run all of them (which is fine). However, for each validation function, it sets or clears that "has-error" class without regard to prior validation results. That might work, if you're really careful with the ordering of the validation functions, but that's an awfully fragile way of doing things. I think it would be much more robust if it made each test and kept track of whether any test failed, and then after that process is complete for a given field it'd only then set or clear the "has-error" class.
Fixing the code isn't too hard. Currently it iterates the the validation functions outside the iteration over the selected fields, which (I think) is backwards. However, as long as it checks the state of the error indicator element(s), it should be OK.
First, at the top, the code removes "has-error" from .form-group elements but not from .input-group elements. That's clearly incorrect, so:
$('.form-control').closest('.form-group, .input-group').removeClass('has-error');
Then, in the loop:
for( z = 0; z < item.selector.length; z++){
selector = $(item.selector[z]); //setting the selector
//attempting to set info to the closest form or input group found by the selector
info = selector.closest('.form-group, .input-group');
if (info.length && !fn(selector.val())) // if info contains data and field is invalid
info.addClass('has-error');
}
Since all the "has-error" flags are cleared at the outset, all we need to do is add the class to classes that are invalid. If you wanted to have a positive "is-ok" class, then you'd add that to everything at the top and remove it when you find an error.
As you should have known, foo.bar are foo["bar"] are identical in JavaScript (if you did not know, learn it, now).
This line
info[fn(selector.val()) ? 'removeClass' : 'addClass']('has-error');
means
var methodName;
if (fn(selector.val())) { methodName = 'removeClass'; } else { methodName = 'addClass'; }
info[methodName]('has-error')
so, in yet another words,
if (fn(selector.val())) {
info.removeClass('has-error');
} else {
info.addClass('has-error');
}
So it is actually switching class has-error on/off. Just it's pretty densely written.
I have some JavaScript that I wrote in a pinch, but I think it could be optimized greatly by someone smarter than me. This code runs on relatively small objects, but it runs a fair amount of times, so its worth getting right:
/**
* Determine the maximum quantity we can show (ever) for these size/color combos
*
* #return int=settings.limitedStockThreshold
*/
function getMaxDefaultQuantity() {
var max_default_quantity = 1;
if (inventory && inventory.sizes) {
sizecolor_combo_loop:
for (var key in inventory.sizes) {
if (inventory.sizes[key].combos) {
for (var key2 in inventory.sizes[key].combos) {
var sizecolor_combo = inventory.sizes[key].combos[key2];
if (isBackorderable(sizecolor_combo)) {
//if even one is backorderable, we can break out
max_default_quantity = settings.limitedStockThreshold;
break sizecolor_combo_loop;
} else {
//not backorderable, get largest quantity (sizecolor_combo or max_default_quantity)
var qoh = parseInt(sizecolor_combo.quantityOnHand || 1);
if (qoh > max_default_quantity) {
max_default_quantity = qoh;
};
};
};
};
};
};
return Math.min(max_default_quantity, settings.limitedStockThreshold);
};
First, inventory is a object returned via JSON. It has a property inventory.sizes that contain all of the available sizes for a product. Each size has a property inventory.sizes.combos which maps to all of the available colors for a size. Each combo also has a property quantityOnHand that tells the quantity available for that specific combo. (the JSON structure returned cannot be modified)
What the code does is loop through each size, then each size's combos. It then checks if the size-color combo is backorderable (via another method). If it any combo is backorderable, we can stop because the default quantity is defined elsewhere. If the combo isn't backorderable, the max_default_quantity is the largest quantityOnHand we find (with a maximum of settings.limitedStockThreshold).
I really don't like the nested for loops and my handling of the math and default values feels overly complicated.
Also, this whole function is wrapped in a much larger jQuery object if that helps clean it up.
Have you considered using map-reduce? See a live example of a functional approach.
This particular example uses underscore.js so we can keep it on a elegant level without having to implement the details.
function doStuff(inventory) {
var max = settings.limitedStockThreshold;
if (!(inventory && inventory.sizes)) return;
var quantity = _(inventory.sizes).chain()
.filter(function(value) {
return value.combos;
})
.map(function(value) {
return _(value.combos).chain()
.map(function(value) {
return isBackorderable(value) ? max : value.quantityOnHand;
})
.max().value();
})
.max().value();
return Math.min(quantity, max);
}
As for an explanation:
We take the inventory.sizes set and remove any that don't contain combos. We then map each size to the maximum quantity of it's colour. We do this mapping each combo to either its quantity or the maximum quantity if backordable. We then take a max of that set.
Finally we take a max of set of maxQuantities per size.
We're still effectily doing a double for loop since we take two .max on the set but it doesn't look as dirty.
There are also a couple of if checks that you had in place that are still there.
[Edit]
I'm pretty sure the above code can be optimized a lot more. but it's a different way of looking at it.
Unfortunately, JavaScript doesn't have much in the way of elegant collection processing capabilities if you have to support older browsers, so without the help of additional libraries, a nested loop like the one you've written is the way to go. You could consider having the values precomputed server-side instead, perhaps cached, and including it in the JSON to avoid having to run the same computations again and again.