I need to increment a number in a url, the number is always preceded by page/ so like page/2/ however the url could be constructed any which way and may even contain other numbers.
Here's what I have which works but increments all numbers rather than just the one that follows page/. What do I need to do to limit it to only the number preceded by page/?
let link = '//localhost:3000/insight/page/2/';
let next = link.replace(/\d+/g, function(n){ return ++n });
console.log(next)
//Outputs '//localhost:3001/insight/page/3/'
//Needs to be '//localhost:3000/insight/page/3/'
Here's a codepen for ease: https://codepen.io/matt3224/pen/brrEQB?editors=1111
Thanks so much
SOLUTION by #adeneo
let link = '//localhost:3000/insight/page/2/';
let next = link.replace(/page\/(\d+)\//, (x,y) => `page/${(++y)}/`);
You could look for a following slash and the end of the string and replace the number.
let link = '//localhost:3000/insight/page/2/';
let next = link.replace(/\d+(?=\/$)/g, n => +n + 1);
console.log(next);
Matching any numbers between page/ and / should work, regardless of other numbers or where in the URL it occurs
let link = '//localhost:3000/insight/page/2/';
let next = link.replace(/page\/(\d+)\//, (x,y) => 'page/' + (++y) + '/');
console.log(next)
test( '//localhost:3000/insight/page/2/' );
test( '//localhost:3000/insight/page/2/more/here' );
test( '//localhost:3000/page/2/' );
test( '//localhost:3000/insight/page/2/2/2/2/' );
function test(link) {
var next = link.replace(/page\/(\d+)\//, (x,y) => 'page/' + (++y) + '/');
console.log(next)
}
Search for page/ followed by a digit, then slice out the digit, increment it, and rebuild the string.
let next = link.replace(/page\/\d+/g, function(n){
let num = parseInt(n.slice(5))
num++
console.log(num)
const result = "page/"+num
return result
});`
As I was typing, adeneo did the same steps much more elegantly, but perhaps my answer will help someone see what's going on.
Related
I want to extract the last part of this string : "https://steamcommunity.com/profiles/76561198364464404".Just the numbers after '/profiles'.But the problem is the URL can change sometimes.
There are two types of url
1.First one is "https://steamcommunity.com/profiles/76561198364464404" with "/profiles" and then the "id"(id is the numbers after '/profiles').
2."https://steamcommunity.com/id/purotexnuk".Second is this type.Where "/profiles" doesn't exist.
I have come up this code :
let inc;
const index = 27;
const string = 'https://steamcommunity.com/id/purotexnuk';
if (string.includes('profiles')) {
inc = 9;
} else {
inc = 3;
}
console.log(string.slice(index + inc, -1));
The above code checks wheather the string "/profiles" is present.If the string contains "/profiles".inc will be 9.So that slice starts from the right side of the string(url) and ends at the first '/' from the right.inc is 9 becuase "profiles/" length is 9.Similar way if the string(url) contains "id".The slice will start from the end and stop at the first '/' from the right.inc will be 3 becuase "id/" length is 3.
The index is always constant because ,"/profiles" or "/id" only occurs after "https://steamcommunity.com" whose length is 27.Is there any better way i can extract only the profile id or profile name?
(profile id - 76561198364464404)
(profile name - purotexnuk )
You can use regex for this, it will also take care if your url ends with / or has query parameters example https://steamcommunity.com/id/purotexnuk?ref=abc
/.*(?:profiles|id)\/([a-z0-9]+)[\/?]?/i
example:
const regex = /.*(?:profiles|id)\/([a-z0-9]+)[\/?]?/i;
const matches = regex.exec('https://steamcommunity.com/id/purotexnuk');
console.log(matches[1]);
You can split the string with delimiter / and return the last value value from the array;
function getNum(str) {
const arr = str.split('/');
if (!isNaN(arr[arr.length - 1])) {
return arr[arr.length - 1];
}
return ' no number ';
}
const st1 = "https://steamcommunity.com/profiles/76561198364464404";
const st2 = "https://steamcommunity.com/profiles/76561198364464404";
const st3 = "https://steamcommunity.com/id/purotexnuk";
console.log(getNum(st1));
console.log(getNum(st2));
console.log(getNum(st3));
Or do it in one line:
const string = 'https://steamcommunity.com/id/purotexnuk';
console.log(string.slice(string.lastIndexOf("/") + 1, string.length));
The initial string:
initString = '/digital/collection/music/bunch/of/other/stuff'
What I want: music
Specifically, I want any term (will never include slashes) that would come between collection/ and /bunch
How I'm going about it:
if(initString.includes('/digital/collection/')){
let slicedString = initString.slice(19); //results in 'music/bunch/of/other/stuff'
let indexOfSlash = slicedString.indexOf('/'); //results, in this case, to 5
let desiredString = slicedString.slice(0, indexOfSlash); //results in 'music'
}
Question:
How the heck do I accomplish this in javascript in a more elegant way?
I looked for something like an endIndexOf() that would replace my hardcoded .slice(19)
lastIndexOf() isn't what I'm looking for, because I want the index at the end of the first instance of my substring /digital/collection/
I'm looking to keep the number of lines down, and I couldn't find anything like a .getStringBetween('beginCutoff, endCutoff')
Thank you in advance!
your title says "index" but your example shows you wanting to return a string. If, in fact, you are wanting to return the string, try this:
if(initString.includes('/digital/collection/')) {
var components = initString.split('/');
return components[3];
}
If the path is always the same, and the field you want is the after the third /, then you can use split.
var initString = '/digital/collection/music/bunch/of/other/stuff';
var collection = initString.split("/")[2]; // third index
In the real world, you will want to check if the index exists first before using it.
var collections = initString.split("/");
var collection = "";
if (collections.length > 2) {
collection = collections[2];
}
You can use const desiredString = initString.slice(19, 24); if its always music you are looking for.
If you need to find the next path param that comes after '/digital/collection/' regardless where '/digital/collection/' lies in the path
first use split to get an path array
then use find to return the element whose 2 prior elements are digital and collection respectively
const initString = '/digital/collection/music/bunch/of/other/stuff'
const pathArray = initString.split('/')
const path = pathArray.length >= 3
? pathArray.find((elm, index)=> pathArray[index-2] === 'digital' && pathArray[index-1] === 'collection')
: 'path is too short'
console.log(path)
Think about this logically: the "end index" is just the "start index" plus the length of the substring, right? So... do that :)
const sub = '/digital/collection/';
const startIndex = initString.indexOf(sub);
if (startIndex >= 0) {
let desiredString = initString.substring(startIndex + sub.length);
}
That'll give you from the end of the substring to the end of the full string; you can always split at / and take index 0 to get just the first directory name form what remains.
You can also use regular expression for the purpose.
const initString = '/digital/collection/music/bunch/of/other/stuff';
const result = initString.match(/\/digital\/collection\/([a-zA-Z]+)\//)[1];
console.log(result);
The console output is:
music
If you know the initial string, and you have the part before the string you seek, then the following snippet returns you the string you seek. You need not calculate indices, or anything like that.
// getting the last index of searchString
// we should get: music
const initString = '/digital/collection/music/bunch/of/other/stuff'
const firstPart = '/digital/collection/'
const lastIndexOf = (s1, s2) => {
return s1.replace(s2, '').split('/')[0]
}
console.log(lastIndexOf(initString, firstPart))
Could someone explain to me, how I can do in javascript this simple code, without taking care of upper and lower case?
if(res.search('em')!=-1){ unit='em'; res.replace(unit,'');}
if(res.search('vh')!=-1){ unit='vh'; res.replace(unit,'');}
if(res.search('px')!=-1){ unit='px'; res.replace(unit,'');}
Without any idea, that is what I have coded. It's a lot of code
if(res.search('Em')!=-1){ unit='Em'; res.replace(unit,'');}
if(res.search('eM')!=-1){ unit='eM'; res.replace(unit,'');}
if(res.search('EM')!=-1){ unit='EM'; res.replace(unit,'');}
...
I'm sure there is a better way to do that!?
Thanks a lot.
You could use a regular expression with replace and save the found unit as a side effect of the replacer function. This would allow you to replace the unit without searching the string twice:
let res = "Value of 200Em etc."
let unit
let newRes = res.replace(/(em|vh|px)/i, (found) => {unit = found.toLowerCase(); return ''})
console.log("replaced:", newRes, "Found Unit:", unit)
For the first part you can use toLowerCase()
if(res.toLowerCase().search('em') != -1)
You can use alternation in regex alongside case insensitive flag.
/(em|vh|px)/i Mathces em or vh or px.
function replaceUnit(input){
return input.replace(/(em|px|vh)/i ,'replaced')
}
console.log(replaceUnit('height: 20em'))
console.log(replaceUnit('width:=20Em'))
console.log(replaceUnit('border-radius: 2Px'))
console.log(replaceUnit('unit=pX'))
console.log(replaceUnit('max-height=20Vh'))
you can use toLowerCase(), transform all the string to lower case and compare,
var tobereplaced = 'em';
if(res.search.toLowerCase(tobereplaced)> -1){ res.replace(tobereplaced,'');}
If you can make these three assumptions:
The string always starts with a number
The string always ends with a unit
The unit is always two characters
Then it could be as simple as:
const str = '11.5px';
const unit = str.substr(-2); //=> 'px'
const value = parseFloat(str, 10); //=> 11.5
Or with a function:
const parse = str => ({unit: str.substr(-2), value: parseFloat(str, 10)});
const {unit, value} = parse('11.5px');
// unit='px', value=11.5
All you need to to is force your string to lowercase (or uppercase) before testing its contents:
if( res.toLowerCase().search('em') !== -1){ do(stuff); }
To handle replacing the actual substring value in res, something like this should work:
let caseInsensitiveUnit = "em";
let unitLength;
let actualUnit;
let position = res.toLowerCase().search(caseInsensitiveUnit);
if(position > -1){
unitLength = caseInsensitiveUnit.length;
actualUnit = res.substring(postion, position + unitLength);
res.replace(actualUnit, "");
}
Assume there are some strings containing names in different format (each line is a possible user input):
'Guilcher, G.M., Harvey, M. & Hand, J.P.'
'Ri Liesner, Peter Tom Collins, Michael Richards'
'Manco-Johnson M, Santagostino E, Ljung R.'
I need to transform those names to get the format Lastname ABC. So each surename should be transformed to its initial which are appended to the lastname.
The example should result in
Guilcher GM, Harvey M, Hand JP
Liesner R, Collins PT, Richards M
Manco-Johnson M, Santagostino E, Ljung R
The problem is the different (possible) input format. I think my attempts are not very smart, so I'm asking for
Some hints to optimize the transformation code
How do I put those in a single function at all? I think first of all I have to test which format the string has...??
So let me explain how far I tried to solve that:
First example string
In the first example there are initials followed by a dot. The dots should be removed and the comma between the name and the initals should be removed.
firstString
.replace('.', '')
.replace(' &', ', ')
I think I do need an regex to get the comma after the name and before the initials.
Second example string
In the second example the name should be splitted by space and the last element is handled as lastname:
const elm = secondString.split(/\s+/)
const lastname = elm[elm.length - 1]
const initials = elm.map((n,i) => {
if (i !== elm.length - 1) return capitalizeFirstLetter(n)
})
return lastname + ' ' + initals.join('')
...not very elegant
Third example string
The third example has the already the correct format - only the dot at the end has to be removed. So nothing else has to be done with that input.
It wouldn't be possible without calling multiple replace() methods. The steps in provided solution is as following:
Remove all dots in abbreviated names
Substitute lastname with firstname
Replace lastnames with their beginning letter
Remove unwanted characters
Demo:
var s = `Guilcher, G.M., Harvey, M. & Hand, J.P.
Ri Liesner, Peter Tom Collins, Michael Richards
Manco-Johnson M, Santagostino E, Ljung R.`
// Remove all dots in abbreviated names
var b = s.replace(/\b([A-Z])\./g, '$1')
// Substitute first names and lastnames
.replace(/([A-Z][\w-]+(?: +[A-Z][\w-]+)*) +([A-Z][\w-]+)\b/g, ($0, $1, $2) => {
// Replace full lastnames with their first letter
return $2 + " " + $1.replace(/\b([A-Z])\w+ */g, '$1');
})
// Remove unwanted preceding / following commas and ampersands
.replace(/(,) +([A-Z]+)\b *[,&]?/g, ' $2$1');
console.log(b);
Given your example data i would try to make guesses based on name part count = 2, since it is very hard to rely on any ,, & or \n - which means treat them all as ,.
Try this against your data and let me know of any use-cases where this fails because i am highly confident that this script will fail at some point with more data :)
let testString = "Guilcher, G.M., Harvey, M. & Hand, J.P.\nRi Liesner, Peter Tom Collins, Michael Richards\nManco-Johnson M, Santagostino E, Ljung R.";
const inputToArray = i => i
.replace(/\./g, "")
.replace(/[\n&]/g, ",")
.replace(/ ?, ?/g, ",")
.split(',');
const reducer = function(accumulator, value, index, array) {
let pos = accumulator.length - 1;
let names = value.split(' ');
if(names.length > 1) {
accumulator.push(names);
} else {
if(accumulator[pos].length > 1) accumulator[++pos] = [];
accumulator[pos].push(value);
}
return accumulator.filter(n => n.length > 0);
};
console.log(inputToArray(testString).reduce(reducer, [[]]));
Here's my approach. I tried to keep it short but complexity was surprisingly high to get the edge cases.
First I'm formatting the input, to replace & for ,, and removing ..
Then, I'm splitting the input by \n, then , and finally (spaces).
Next I'm processing the chunks. On each new segment (delimited by ,), I process the previous segment. I do this because I need to be sure that the current segment isn't an initial. If that's the case, I do my best to skip that inital-only segment and process the previous one. The previous one will have the correct initial and surname, as I have all the information I neeed.
I get the initial on the segment if there's one. This will be used on the start of the next segment to process the current one.
After finishing each line, I process again the last segment, as it wont be called otherwise.
I understand the complexity is high without using regexp, and probably would have been better to use a state machine to parse the input instead.
const isInitial = s => [...s].every(c => c === c.toUpperCase());
const generateInitial = arr => arr.reduce((a, c, i) => a + (i < arr.length - 1 ? c[0].toUpperCase() : ''), '');
const formatSegment = (words, initial) => {
if (!initial) {
initial = generateInitial(words);
}
const surname = words[words.length - 1];
return {initial, surname};
}
const doDisplay = x => x.map(x => x.surname + ' ' + x.initial).join(', ');
const doProcess = _ => {
const formatted = input.value.replace(/\./g, '').replace(/&/g, ',');
const chunks = formatted.split('\n').map(x => x.split(',').map(x => x.trim().split(' ')));
const peoples = [];
chunks.forEach(line => {
let lastSegment = null;
let lastInitial = null;
let lastInitialOnly = false;
line.forEach(segment => {
if (lastSegment) {
// if segment only contains an initial, it's the initial corresponding
// to the previous segment
const initialOnly = segment.length === 1 && isInitial(segment[0]);
if (initialOnly) {
lastInitial = segment[0];
}
// avoid processing last segments that were only initials
// this prevents adding a segment twice
if (!lastInitialOnly) {
// if segment isn't an initial, we need to generate an initial
// for the previous segment, if it doesn't already have one
const people = formatSegment(lastSegment, lastInitial);
peoples.push(people);
}
lastInitialOnly = initialOnly;
// Skip initial only segments
if (initialOnly) {
return;
}
}
lastInitial = null;
// Remove the initial from the words
// to avoid getting the initial calculated for the initial
segment = segment.filter(word => {
if (isInitial(word)) {
lastInitial = word;
return false;
}
return true;
});
lastSegment = segment;
});
// Process last segment
if (!lastInitialOnly) {
const people = formatSegment(lastSegment, lastInitial);
peoples.push(people);
}
});
return peoples;
}
process.addEventListener('click', _ => {
const peoples = doProcess();
const display = doDisplay(peoples);
output.value = display;
});
.row {
display: flex;
}
.row > * {
flex: 1 0;
}
<div class="row">
<h3>Input</h3>
<h3>Output</h3>
</div>
<div class="row">
<textarea id="input" rows="10">Guilcher, G.M., Harvey, M. & Hand, J.P.
Ri Liesner, Peter Tom Collins, Michael Richards
Manco-Johnson M, Santagostino E, Ljung R.
Jordan M, Michael Jackson & Willis B.</textarea>
<textarea id="output" rows="10"></textarea>
</div>
<button id="process" style="display: block;">Process</button>
I am new in programing and right now I am working on one program. Program need to find the substring in a string and return the index where the chain starts to be the same. I know that for that I can use "indexOf". Is not so easy. I want to find out substrings with at moste one different char.
I was thinking about regular expresion... but not really know how to use it because I need to use regular expresion for every element of the string. Here some code wich propably will clarify what I want to do:
var A= "abbab";
var B= "ba";
var tb=[];
console.log(A.indexOf(B));
for (var i=0;i<B.length; i++){
var D=B.replace(B[i],"[a-z]");
tb.push(A.indexOf(D));
}
console.log(tb);
I know that the substring B and string A are the lowercase letters. Will be nice to get any advice how to make it using regular expresions. Thx
Simple Input:
A B
1) abbab ba
2) hello world
3) banana nan
Expected Output:
1) 1 2
2) No Match!
3) 0 2
While probably theoretically possible, I think it would very complicated to try this kind of search while attempting to incorporate all possible search query options in one long complex regular expression. I think a better approach is to use JavaScript to dynamically create various simpler options and then search with each separately.
The following code sequentially replaces each character in the initial query string with a regular expression wild card (i.e. a period, '.') and then searches the target string with that. For example, if the initial query string is 'nan', it will search with '.an', 'n.n' and 'na.'. It will only add the position of the hit to the list of hits if that position has not already been hit on a previous search. i.e. It ensures that the list of hits contains only unique values, even if multiple query variations found a hit at the same location. (This could be implemented even better with ES6 sets, but I couldn't get the Stack Overflow code snippet tool to cooperate with me while trying to use a set, even with the Babel option checked.) Finally, it sorts the hits in ascending order.
Update: The search algorithm has been updated/corrected. Originally, some hits were missed because the exec search for any query variation would only iterate as per the JavaScript default, i.e. after finding a match, it would start the next search at the next character after the end of the previous match, e.g. it would find 'aa' in 'aaaa' at positions 0 and 2. Now it starts the next search at the next character after the start of the previous match, e.g. it now finds 'aa' in 'aaaa' at positions 0, 1 and 2.
const findAllowingOneMismatch = (target, query) => {
const numLetters = query.length;
const queryVariations = [];
for (let variationNum = 0; variationNum < numLetters; variationNum += 1) {
queryVariations.push(query.slice(0, variationNum) + "." + query.slice(variationNum + 1));
};
let hits = [];
queryVariations.forEach(queryVariation => {
const re = new RegExp(queryVariation, "g");
let myArray;
while ((searchResult = re.exec(target)) !== null) {
re.lastIndex = searchResult.index + 1;
const hit = searchResult.index;
// console.log('found a hit with ' + queryVariation + ' at position ' + hit);
if (hits.indexOf(hit) === -1) {
hits.push(searchResult.index);
}
}
});
hits = hits.sort((a,b)=>(a-b));
console.log('Found "' + query + '" in "' + target + '" at positions:', JSON.stringify(hits));
};
[
['abbab', 'ba'],
['hello', 'world'],
['banana', 'nan'],
['abcde abcxe abxxe xbcde', 'abcd'],
['--xx-xxx--x----x-x-xxx--x--x-x-xx-', '----']
].forEach(pair => {findAllowingOneMismatch(pair[0], pair[1])});