I get the name of an input element, which is a string with a number (url1). I want to increment the number by 1 (url2) in the easiest and quickest way possible.
My way would be to get \d / restofstring, ++ the match, then put together number with restofstring. Is there a better way?
Update:
My final (dummy)code became:
var liNew = document.createElement('li');
liNew.innerHTML = liOld.innerHTML;
var els = Y.Dom.getChildrenBy(liNew, function(el) {
return el.name.match(/\d+$/);
} // YUI method where the function is a test
for (var i = 0, el; el = els[i]; i++) {
el.name = el.name.replace(/\d+$/, function(n) { return ++n });
}
list.appendChild(liNew);
How about:
'url1'.replace(/\d+$/, function(n){ return ++n }); // "url2"
'url54'.replace(/\d+$/, function(n){ return ++n }); // "url55"
There we search for a number at the end of the string, cast it to Number, increment it by 1, and place it back in the string. I think that's the same algo you worded in your question even.
Reference:
String.prototype.replace - can take a regex
Simple. Use a substitution function with regular expressions:
s = 'abc99abc';
s = s.replace(/\d+/, function(val) { return parseInt(val)+1; });
will set variable s to: abc100abc
But it gets more complicated if you want to make sure you only change a certain parameter in the URL:
s = '?foo=10&bar=99';
s = s.replace(/[&?]bar=\d+/, function(attr) {
return attr.replace(/\d+/, function(val) { return parseInt(val)+1; });
});
will set variable s to: ?foo=10&bar=100
Looks OK. You might want to use a regex like ^(.*?)(\d+)$, making sure the number you're grabbing is at the end of the string.
You can use replace and pass it a function to use to replace the matched section:
str.replace(/\d+/, function(number) { return parseInt(number, 10) + 1; });
Related
i have tried to make a function count each character in a string using recursion, for 2 days now. I tried to write some pseudo-code, but i can't really implement it.
Pseudocode:
write a function that takes text as a parameter
set a counter, for each element
set a result, using key,value for each character in element
base case: if we only have 1 string, then return the character and string
else return function-1 until the last element is hit.
var tekst = "We have to count strings";
function countStrings(tekst) {
var count = 0
var result = {}
if (count > tekst.lentgh) {
count++
return result
} else {
return countStrings(tekst-1)
}
}
console.log(countStrings(tekst))
Consider using this logic:
var tekst = "We have to count strings";
function countStrings(tekst) {
if (tekst.length == 0) {
return 0;
}
return 1 + countStrings(tekst.substring(1));
}
console.log(countStrings(tekst))
The approach here is, at each step of the recursion, to return 1 plus whatever the length of the substring from the next character onwards is. That is, we recurse down the input string, one character at a time, building out the length. The base case here occurs when the input to countStrings() happens to be empty string. In this case, we just return 0, and stop the recursive calls.
I decided to attempt this problem and this is what I came up with. Definitely a challenging problem so don't feel bad if you didn't get it:
var countStrings = function(tekst) {
if (tekst.length === 0) return {};
var obj = countStrings(tekst.slice(1));
if (obj[tekst[0]]) {
obj[tekst[0]] += 1;
} else {
obj[tekst[0]] = 1;
}
return obj;
};
I want to implement the split method with a function
This is what i am trying to achieve
var string = 'aa,bb,c';
var separator = ',';
var stringList = string.split(separator);
function splitString() {
console.log(stringList);
}
This returns this array
["aa", "bb", "c"]
I am trying to implement the same with a function but it returns an empty array [] and not ["aa", "bb", "c"]
I have created a jsbin for who can help out.
function split(string,separator) {
var cache = [];
var cachInt = 0;
var lastWord = '';
for (var i = 0; i < string.length; i++) {
if(string[i] === separator) {
cachInt++
lastWord = ''
}
else {
lastWord = lastWord + string[i];
cache[cachInt] == lastWord;
}
}
return cache;
}
function splitString() {
console.log(split('string, separator',','));
}
You do this:
cache[cachInt] == lastWord;
Which should be, because you're not comparing, you're assigning:
cache[cachInt] = lastWord;
While we're at it, there is room for improvement. Your version has the line mentioned above. That line gets run every iteration of i. Thats not really needed, as you only want to perform a save on a split:
if(string[i] === separator) {
cache[cachInt] = lastWord; // Only assign when we find a seperator
cachInt++
lastWord = ''
} else {
lastWord = lastWord + string[i];
}
This has a tiny issue: The last part of string often doesn't have the seperator, it's a,b,c and not a,b,c,.
We can fix that easily with a check after the for to see if you have anything remaining:
if( lastWord!=='' ){
cache[cachInt] = lastWord;
}
return cache;
This has the added feature that it works as a rtrim() (wether you want that or not is up to you to fix).
Also, if you don't need to support older IE versions, then don't use var, use let. If you want to know why, this question explains it well.
Then, you're using a counter to remember which cachInt to use. As we now only use it once per "cacheInt", eg once per word, we know that each addition is +1, and only happens once per word. We also don't really care about the index, we just want each word to be added once. So, you can do cache[] = lastWord, or use push, which is slightly neater: cache.push(lastWord).
By removing the use for this counter, you can also remove the cachInt++ and the let/var cachInt at the beginning of the function, resulting in smaller code.
Result of all of the above:
https://jsbin.com/mejayuv/1/edit?html,js,console,output
Not sure why this is happening. At this point in my code these array may have a single element.
actualAnswer = actualAnswer.split(" ");
playerAnswer = playerAnswer.split(" ");
I then pass them through this function.
function checkForPlural(playerAnswer, actualAnswer){
var answerObject = {playerAnswer: playerAnswer,
actualAnswer: actualAnswer};
for (var answerWord in actualAnswer){
if (actualAnswer[answerWord].slice(-1) == "S")
{
answerObject.actualAnswer[answerWord] = actualAnswer[answerWord].substring(0, actualAnswer[answerWord].length - 1);
}
}
for (var answerWord in playerAnswer){
if (playerAnswer[answerWord].slice(-1) == "S")
{
answerObject.playerAnswer[answerWord] = playerAnswer[answerWord].substring(0, playerAnswer[answerWord].length - 1);
}
}
}
When I return the object IF the array's passed in only had a single element javascript decides to interpret them as a string so if I were to use answerObject.actualAnswer.length it'll give me the length of the string, rather than just 1.
Here is the plunker, unfortunately/fortunately it seems to work as intended on there so i'm confused:
https://plnkr.co/edit/nIqq3VdjjMa2GDWyqkxx
EDIT: The problem was at an earlier junction in my code, sorry!
You can use Array.prototype.map() to return a new array, String.prototype.replace() to, if exists, replace the ending "S" character in a string, return an object having properties set to the function parameter identifiers
const regexp = /S$/;
const re = "";
function parseAnswer(arr, regexp_, re_) {
return arr.map(function(prop) {return prop.replace(regexp_, re_)})
}
function checkForPlural(playerAnswer, actualAnswer) {
return {
playerAnswer: parseAnswer(playerAnswer, regexp, re),
actualAnswer: parseAnswer(actualAnswer, regexp, re)
}
}
console.log(checkForPlural("abc defS ghi".split(" "), "12S 456 78S".split(" ")))
The code in question is working correctly, the "magic" conversion was because of an error in another function that was overwriting the array with a string. My bad.
I need a regular expression in javascript that will get a string with a specific substring from a list of space delimited strings.
For example, I have;
widget util cookie i18n-username
I want to be able to return only i18n-username.
How
You could use the following function, using a regex to match for your string surrounded by either a space or the beginning or end of a line. But you'll have to be careful about preparing any regular expression special characters if you plan to use them, since the search argument will be interpreted as a string instead of a RegExp literal:
var hasClass = function(s, klass) {
var r = new RegExp("(?:^| )(" + klass + ")(?: |$)")
, m = (""+s).match(r);
return (m) ? m[1] : null;
};
hasClass("a b c", "a"); // => "a"
hasClass("a b c", "b"); // => "b"
hasClass("a b c", "x"); // => null
var klasses = "widget util cookie i18n-username";
hasClass(klasses, "username"); // => null
hasClass(klasses, "i18n-username"); // => "i18n-username"
hasClass(klasses, "i18n-\\w+"); // => "i18n-username"
As others have pointed out, you could also simply use a "split" and "indexOf":
var hasClass = function(s, klass) {
return (""+s).split(" ").indexOf(klass) >= 0;
};
However, note that the "indexOf" function was introduced to JavaScript somewhat recently, so for older browsers you might have to implement it yourself.
var hasClass = function(s, klass) {
var a=(""+s).split(" "), len=a.length, i;
for (i=0; i<len; i++) {
if (a[i] == klass) return true;
}
return false;
};
[Edit]
Note that the split/indexOf solution is likely faster for most browsers (though not all). This jsPerf benchmark shows which solution is faster for various browsers - notably, Chrome must have a really good regular expression engine!
function getString(subString, string){
return (string.match(new RegExp("\S*" + subString + "\S*")) || [null])[0];
}
To Use:
var str = "widget util cookie i18n-username";
getString("user", str); //returns i18n-username
Does this need to be a regex? Would knowing if the string existed be sufficient? Regular expressions are inefficient (slower) and should be avoided if possible:
var settings = 'widget util cookie i18n-username',
// using an array in case searching the string is insufficient
features = settings.split(' ');
if (features.indexOf('i18n-username') !== -1) {
// do something based on having this feature
}
If whitespace wouldn't cause an issue in searching for a value, you could just search the string directly:
var settings = 'widget util cookie i18n-username';
if (settings.indexOf('i18n-username') !== -1) {
// do something based on having this value
}
It then becomes easy to make this into a reusable function:
(function() {
var App = {},
features = 'widget util cookie i18n-username';
App.hasFeature = function(feature) {
return features.indexOf(feature) !== -1;
// or if you prefer the array:
return features.split(' ').indexOf(feature) !== -1;
};
window.App = App;
})();
// Here's how you can use it:
App.hasFeature('i18n-username'); // returns true
EDIT
You now say you need to return all strings that start with another string, and it is possible to do this with a regular expression as well, although I am unsure about how efficient it is:
(function() {
var App = {},
features = 'widget util cookie i18n-username'.split(' ');
// This contains an array of all features starting with 'i18n'
App.i18nFeatures = features.map(function(value) {
return value.indexOf('i18n') === 0;
});
window.App = App;
})();
/i18n-\w+/ ought to work. If your string has any cases like other substrings can start with i18n- or your user names have chars that don't fit the class [a-zA-Z0-9_], you'll need to specify that.
var str = "widget util cookie i18n-username";
alert(str.match(/i18n-\w+/));
Edit:
If you need to match more than one string, you can add on the global flag (/g) and loop through the matches.
var str = "widget i18n-util cookie i18n-username";
var matches = str.match(/i18n-\w+/g);
if (matches) {
for (var i = 0; i < matches.length; i++)
alert(matches[i]);
}
else
alert("phooey, no matches");
I'm trying to write a regex function that will identify and replace a single instance of a match within a string without affecting the other instances. For example, I have this string:
12||34||56
I want to replace the second set of pipes with ampersands to get this string:
12||34&&56
The regex function needs to be able to handle x amount of pipes and allow me to replace the nth set of pipes, so I could use the same function to make these replacements:
23||45||45||56||67 -> 23&&45||45||56||67
23||34||98||87 -> 23||34||98&&87
I know that I could just split/replace/concat the string at the pipes, and I also know that I can match on /\|\|/ and iterate through the resulting array, but I'm interested to know if it's possible to write a single expression that can do this. Note that this would be for Javascript, so it's possible to generate a regex at runtime using eval(), but it's not possible to use any Perl-specific regex instructions.
A more general-purpose function
I came across this question and, although the title is very general, the accepted answer handles only the question's specific use case.
I needed a more general-purpose solution, so I wrote one and thought I'd share it here.
Usage
This function requires that you pass it the following arguments:
original: the string you're searching in
pattern: either a string to search for, or a RegExp with a capture group. Without a capture group, it will throw an error. This is because the function calls split on the original string, and only if the supplied RegExp contains a capture group will the resulting array contain the matches.
n: the ordinal occurrence to find; eg, if you want the 2nd match, pass in 2
replace: Either a string to replace the match with, or a function which will take in the match and return a replacement string.
Examples
// Pipe examples like the OP's
replaceNthMatch("12||34||56", /(\|\|)/, 2, '&&') // "12||34&&56"
replaceNthMatch("23||45||45||56||67", /(\|\|)/, 1, '&&') // "23&&45||45||56||67"
// Replace groups of digits
replaceNthMatch("foo-1-bar-23-stuff-45", /(\d+)/, 3, 'NEW') // "foo-1-bar-23-stuff-NEW"
// Search value can be a string
replaceNthMatch("foo-stuff-foo-stuff-foo", "foo", 2, 'bar') // "foo-stuff-bar-stuff-foo"
// No change if there is no match for the search
replaceNthMatch("hello-world", "goodbye", 2, "adios") // "hello-world"
// No change if there is no Nth match for the search
replaceNthMatch("foo-1-bar-23-stuff-45", /(\d+)/, 6, 'NEW') // "foo-1-bar-23-stuff-45"
// Passing in a function to make the replacement
replaceNthMatch("foo-1-bar-23-stuff-45", /(\d+)/, 2, function(val){
//increment the given value
return parseInt(val, 10) + 1;
}); // "foo-1-bar-24-stuff-45"
The Code
var replaceNthMatch = function (original, pattern, n, replace) {
var parts, tempParts;
if (pattern.constructor === RegExp) {
// If there's no match, bail
if (original.search(pattern) === -1) {
return original;
}
// Every other item should be a matched capture group;
// between will be non-matching portions of the substring
parts = original.split(pattern);
// If there was a capture group, index 1 will be
// an item that matches the RegExp
if (parts[1].search(pattern) !== 0) {
throw {name: "ArgumentError", message: "RegExp must have a capture group"};
}
} else if (pattern.constructor === String) {
parts = original.split(pattern);
// Need every other item to be the matched string
tempParts = [];
for (var i=0; i < parts.length; i++) {
tempParts.push(parts[i]);
// Insert between, but don't tack one onto the end
if (i < parts.length - 1) {
tempParts.push(pattern);
}
}
parts = tempParts;
} else {
throw {name: "ArgumentError", message: "Must provide either a RegExp or String"};
}
// Parens are unnecessary, but explicit. :)
indexOfNthMatch = (n * 2) - 1;
if (parts[indexOfNthMatch] === undefined) {
// There IS no Nth match
return original;
}
if (typeof(replace) === "function") {
// Call it. After this, we don't need it anymore.
replace = replace(parts[indexOfNthMatch]);
}
// Update our parts array with the new value
parts[indexOfNthMatch] = replace;
// Put it back together and return
return parts.join('');
}
An Alternate Way To Define It
The least appealing part of this function is that it takes 4 arguments. It could be simplified to need only 3 arguments by adding it as a method to the String prototype, like this:
String.prototype.replaceNthMatch = function(pattern, n, replace) {
// Same code as above, replacing "original" with "this"
};
If you do that, you can call the method on any string, like this:
"foo-bar-foo".replaceNthMatch("foo", 2, "baz"); // "foo-bar-baz"
Passing Tests
The following are the Jasmine tests that this function passes.
describe("replaceNthMatch", function() {
describe("when there is no match", function() {
it("should return the unmodified original string", function() {
var str = replaceNthMatch("hello-there", /(\d+)/, 3, 'NEW');
expect(str).toEqual("hello-there");
});
});
describe("when there is no Nth match", function() {
it("should return the unmodified original string", function() {
var str = replaceNthMatch("blah45stuff68hey", /(\d+)/, 3, 'NEW');
expect(str).toEqual("blah45stuff68hey");
});
});
describe("when the search argument is a RegExp", function() {
describe("when it has a capture group", function () {
it("should replace correctly when the match is in the middle", function(){
var str = replaceNthMatch("this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
expect(str).toEqual("this_937_thing_NEW_has_21_numbers");
});
it("should replace correctly when the match is at the beginning", function(){
var str = replaceNthMatch("123_this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
expect(str).toEqual("123_this_NEW_thing_38_has_21_numbers");
});
});
describe("when it has no capture group", function() {
it("should throw an error", function(){
expect(function(){
replaceNthMatch("one_1_two_2", /\d+/, 2, 'NEW');
}).toThrow('RegExp must have a capture group');
});
});
});
describe("when the search argument is a string", function() {
it("should should match and replace correctly", function(){
var str = replaceNthMatch("blah45stuff68hey", 'stuff', 1, 'NEW');
expect(str).toEqual("blah45NEW68hey");
});
});
describe("when the replacement argument is a function", function() {
it("should call it on the Nth match and replace with the return value", function(){
// Look for the second number surrounded by brackets
var str = replaceNthMatch("foo[1][2]", /(\[\d+\])/, 2, function(val) {
// Get the number without the [ and ]
var number = val.slice(1,-1);
// Add 1
number = parseInt(number,10) + 1;
// Re-format and return
return '[' + number + ']';
});
expect(str).toEqual("foo[1][3]");
});
});
});
May not work in IE7
This code may fail in IE7 because that browser incorrectly splits strings using a regex, as discussed here. [shakes fist at IE7]. I believe that this is the solution; if you need to support IE7, good luck. :)
here's something that works:
"23||45||45||56||67".replace(/^((?:[0-9]+\|\|){n})([0-9]+)\|\|/,"$1$2&&")
where n is the one less than the nth pipe, (of course you don't need that first subexpression if n = 0)
And if you'd like a function to do this:
function pipe_replace(str,n) {
var RE = new RegExp("^((?:[0-9]+\\|\\|){" + (n-1) + "})([0-9]+)\|\|");
return str.replace(RE,"$1$2&&");
}
function pipe_replace(str,n) {
m = 0;
return str.replace(/\|\|/g, function (x) {
//was n++ should have been m++
m++;
if (n==m) {
return "&&";
} else {
return x;
}
});
}
Thanks Binda, I have modified the code for generic uses:
private replaceNthMatch(original, pattern, n, replace) {
let m = -1;
return original.replaceAll(pattern, x => {
m++;
if ( n == m ) {
return replace;
} else {
return x;
}
});
}