I am trying to make a function to parse command line arguments from a text file. This means each flag and value need to be returned as separate items in one array. Lines should be ignored if they are empty or start with #, ; or ].
I am having multiple issues with my current function. First, splitting arrays inside the reduce function does not add arrays to the accumulator like using push would, but adds a new array to the accumulator. Second, strings inside quotes can be split into arrays even thought they should be treated as single arguments.
const argsFile = `
# Command line arguments
--download example.com
--pass
--no-fail
--output "C:\\Users\\User\\Desktop\\New Folder"
--binary-location 'C:\\Users\\Desktop\\New Folder\\executable program.exe'
`;
let parsedArguments = argsFile.split(/\r?\n/)
.filter(argument => (!argument.startsWith('#') && !argument.startsWith(';') && !argument.startsWith(']')))
.reduce((a, c) => [...a, c.split(' ')])
.filter(argument => argument !== '');
console.dir(parsedArguments)
This is the desired output for my function:
[
"--download",
"example.com",
"--pass",
"--no-fail",
"--output",
"C:\\Users\\User\\Desktop\\New Folder",
"--binary-location",
"C:\\Users\\Desktop\\New Folder\\executable program.exe"
]
How can I modify my function to achieve the desired output? If there is a library that would handle this situation I have not been able to find it.
Yargs seems to parse arguments for string quite robustly and it's rather configurable.
I came up with the following, which seems to yield the output you desired. Didn't test it with any other strings, however:
const parse = require("yargs-parser");
const argsFile = `
# Command line arguments
--download example.com
--pass
--no-fail
--output "C:\\Users\\User\\Desktop\\New Folder"
--binary-location 'C:\\Users\\Desktop\\New Folder\\executable program.exe'
`;
let parsedArguments = argsFile
.split(/\r?\n/)
.filter(
(argument) =>
!argument.startsWith("#") &&
!argument.startsWith(";") &&
!argument.startsWith("]")
)
.map((line) => {
return parse(line, {
string: true,
configuration: {
"boolean-negation": false,
"camel-case-expansion": false,
},
});
})
.map((ar) => {
delete ar._;
let properties = Object.keys(ar);
if (properties.length == 0) return [];
return [
"--" + properties[0],
typeof ar[properties[0]] == "boolean" ? "" : ar[properties[0]],
];
})
.filter((argument) => argument.length != 0);
let flatArgs = [].concat.apply([], parsedArguments).filter((i) => i != "");
console.dir(flatArgs);
Yields the following:
[ '--download',
'example.com',
'--pass',
'--no-fail',
'--output',
'C:\\Users\\User\\Desktop\\New Folder',
'--binary-location',
'C:\\Users\\Desktop\\New Folder\\executable program.exe' ]
Yargs parser parses the string "too aggressively" for your particular requirements, that's why we have to do the mapping with kinda reversing what parser has done (prepending with '--', ignoring booleans etc.). Then, at the end, we have to "flatten" the array, as each line is being parsed to its own array.
Edit: so, if we've to take care of short args as well, yargs will not really be suitable as we do not have an access to raw string after parsing. We could make use of yarg's internal function which tokenizes the string, however (I only had to transpile it to js):
function tokenizeArgString(argString) {
if (Array.isArray(argString)) {
return argString.map(e => typeof e !== 'string' ? e + '' : e);
}
argString = argString.trim();
let i = 0;
let prevC = null;
let c = null;
let opening = null;
const args = [];
for (let ii = 0; ii < argString.length; ii++) {
prevC = c;
c = argString.charAt(ii);
// split on spaces unless we're in quotes.
if (c === ' ' && !opening) {
if (!(prevC === ' ')) {
i++;
}
continue;
}
// don't split the string if we're in matching
// opening or closing single and double quotes.
if (c === opening) {
opening = null;
}
else if ((c === "'" || c === '"') && !opening) {
opening = c;
}
if (!args[i])
args[i] = '';
args[i] += c;
}
return args;
}
const argsFile = `
# Command line arguments
--download example.com
--pass
--no-fail
--output "C:\\Users\\User\\Desktop\\New Folder"
-a test
--binary-location 'C:\\Users\\Desktop\\New Folder\\executable program.exe'
`;
let parsedArguments = argsFile.split(/\r?\n/)
.filter(argument => (!argument.startsWith('#') && !argument.startsWith(';') && !argument.startsWith(']')))
.map(line => tokenizeArgString(line))
.filter(argument => argument.length != 0);
let flatArgsNoQuotes = [].concat.apply([], parsedArguments).map(args => args.replace(/['"]+/g, '')).filter(i => i != "");
console.dir(flatArgsNoQuotes)
This question was successfully answered for C language but not for JS as of yet.
Write a function that can find if a string is a sub-string of another. Note that a mismatch of one character should be ignored.
A mismatch can be an extra character: ’dog’ matches ‘xxxdoogyyyy’
A mismatch can be a missing character: ’dog’ matches ‘xxxdgyyyy’
A mismatch can be a different character: ’dog’ matches ‘xxxdigyyyy’
Existing fuzzy search modules are much too complex and unpredictable for this specific case. How to write such function in JS?
Note: Here is the original question and accepted solution by #IVlad (2016) in C language :
int findHelper(const char *str, const char *substr, int mustMatch = 0)
{
if ( *substr == '\0' )
return 1;
if ( *str == '\0' )
return 0;
if ( *str == *substr )
return findHelper(str + 1, substr + 1, mustMatch);
else
{
if ( mustMatch )
return 0;
if ( *(str + 1) == *substr )
return findHelper(str + 1, substr, 1);
else if ( *str == *(substr + 1) )
return findHelper(str, substr + 1, 1);
else if ( *(str + 1) == *(substr + 1) )
return findHelper(str + 1, substr + 1, 1);
else if ( *(substr + 1) == '\0' )
return 1;
else
return 0;
}
}
int find(const char *str, const char *substr)
{
int ok = 0;
while ( *str != '\0' )
ok |= findHelper(str++, substr, 0);
return ok;
}
int main()
{
printf("%d\n", find("xxxdoogyyyy", "dog"));
printf("%d\n", find("xxxdgyyyy", "dog"));
printf("%d\n", find("xxxdigyyyy", "dog"));
}
you can do this using regex generator like this
Edit : add another test cases
Edit #2: as #amadan suggest, add ?.? for char insertion scenario
var txtToSearch = 'dog';
function find(txt,src) {
// generate regex for all possibilities. for this case, it will generate "d?.?og|do?.?g|dog?.?" -> double .? are for 1 char insertion
var re = new RegExp(txt.split('').map(function(a,b,c){ return txt.substr(0, b)+a+'?.?'+ txt.substr(b+1);}).join('|'),'gi');
return src.match(re)!=null
}
// test cases
console.log('"'+txtToSearch+'" in : xxxdoogyyyy -> '+find(txtToSearch,'xxxdoogyyyy'));
console.log('"'+txtToSearch+'" in : xxxdgyyyy -> '+find(txtToSearch,'xxxdgyyyy'));
console.log('"'+txtToSearch+'" in : xxxdigyyyy -> '+find(txtToSearch,'xxxdigyyyy'));
console.log('"'+txtToSearch+'" in : xxxggoodyyyy -> '+find(txtToSearch,'xxxggoodyyyy'));
// another test case
console.log('"great" in : xxzzgreetsxxy -> '+find('great','xxzzgreetsxxy'));
console.log('"greetings" in : xxzzgreextingssxxy-> '+find('greetings','xxzzgreextingssxxy'));
I created my own version, that also removes special characters from the input, just in case we don't want extra matches when writing '...' that would match everything.
export const escapedRegexCharacters = /[\\.+=?!:[\](){}\-$*&|/]/g;
export const find = (word, source, options = { typos: 1, minChars: 3 }) => {
const expressions = [];
const { typos = 1, minChars = 3 } = options;
const trimmed = String(word).trim();
const replaced = trimmed.replace(escapedRegexCharacters, '');
const chopped = replaced.split('');
if (replaced.length < typos + 1 || replaced.length < minChars) return true;
if (!typos) return source.match(new RegExp(replaced, 'gi'));
for (let index = 0; index < chopped.length; index += 1) {
const parts = [...chopped];
parts.splice(index, 1, `.{${typos}}`);
expressions.push(parts.join(''));
}
const finalExpression = expressions.join('|');
return source.match(new RegExp(finalExpression, 'gi'));
};
This question already has answers here:
Get index of each capture in a JavaScript regex
(7 answers)
Closed 1 year ago.
When I write a regular expression like:
var m = /(s+).*?(l)[^l]*?(o+)/.exec("this is hello to you");
console.log(m);
I get a match object containing the following:
{
0: "s is hello",
1: "s",
2: "l",
3: "o",
index: 3,
input: "this is hello to you"
}
I know the index of the entire match from the index property, but I also need to know the start and end of the groups matched. Using a simple search won't work. In this example it will find the first 'l' instead of the one found in the group.
Is there any way to get the offset of a matched group?
You can't directly get the index of a match group. What you have to do is first put every character in a match group, even the ones you don't care about:
var m= /(s+)(.*?)(l)([^l]*?)(o+)/.exec('this is hello to you');
Now you've got the whole match in parts:
['s is hello', 's', ' is hel', 'l', '', 'o']
So you can add up the lengths of the strings before your group to get the offset from the match index to the group index:
function indexOfGroup(match, n) {
var ix= match.index;
for (var i= 1; i<n; i++)
ix+= match[i].length;
return ix;
}
console.log(indexOfGroup(m, 3)); // 11
I wrote a simple (well the initialization got a bit bloated) javascript object to solve this problem on a project I've been working on recently. It works the same way as the accepted answer but generates the new regexp and pulls out the data you requested automatically.
var exp = new MultiRegExp(/(firstBit\w+)this text is ignored(optionalBit)?/i);
var value = exp.exec("firstbitWithMorethis text is ignored");
value = {0: {index: 0, text: 'firstbitWithMore'},
1: null};
Git Repo: My MultiRegExp. Hope this helps someone out there.
edit Aug, 2015:
Try me: MultiRegExp Live.
Another javascript class which is also able to parse nested groups is available under: https://github.com/valorize/MultiRegExp2
Usage:
let regex = /a(?: )bc(def(ghi)xyz)/g;
let regex2 = new MultiRegExp2(regex);
let matches = regex2.execForAllGroups('ababa bcdefghixyzXXXX'));
Will output:
[ { match: 'defghixyz', start: 8, end: 17 },
{ match: 'ghi', start: 11, end: 14 } ]
I have played around with adding nested capture groups and named groups with position information.
You can play with some regex on jsfiddle...
https://jsfiddle.net/smuchow1962/z5dj9gL0/
/*
Copyright (c) 2019 Steven A Muchow
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Enhanced RegEx JS processing
Adds position information for capture groups (nested ones too) AND named group items.
*/
class RegexContainer {
static _findCaptureGroupsInRegexTemplate(re, input) {
let refCount = 0; let matches = []; let res; let data;
re.lastIndex = 0;
while ((res = re.exec(input)) !== null) {
if (isCapturingStartItem(res[0])) {
refCount++;
data = {parent: 0, refCount: refCount, start: res.index};
if (res.groups.name) { data.name = res.groups.name; }
matches.push(data);
} else if (input.charAt(res.index) === ')') {
let idx = matches.length;
while (idx--) {
if (matches[idx].end === undefined) {
matches[idx].end = re.lastIndex;
matches[idx].source = input.substring(matches[idx].start, matches[idx].end);
break;
}
}
refCount--;
let writeIdx = idx;
while (idx--) {
if (matches[idx].refCount === refCount) {
matches[writeIdx].parent = idx + 1;
break;
}
}
}
}
matches.unshift({start: 0, end: input.length, source: input});
return matches;
function isCapturingStartItem(str) {
if (str !== '(') { return (str.search(/\(\?<\w/)!==-1); }
return true;
}
}
static execFull(re, input, foundCaptureItems) {
let result; let foundIdx; let groupName; const matches = [];
while ((result = re.exec(input)) !== null) {
let array = createCustomResultArray(result);
array.forEach((match, idx) => {
if (!idx) {
match.startPos = match.endPos = result.index;
match.endPos += result[0].length;
delete match.parent;
return;
}
let parentStr = array[match.parent].data;
foundIdx = (match.parent < idx - 1) ? parentStr.lastIndexOf(match.data) : parentStr.indexOf(match.data);
match.startPos = match.endPos = foundIdx + array[match.parent].startPos;
match.endPos += match.data.length;
if ((groupName = foundCaptureItems[idx].name)) { match.groupName = groupName; }
});
matches.push(array);
if (re.lastIndex === 0) { break; }
}
return matches;
function createCustomResultArray(result) {
let captureVar = 0;
return Array.from(result, (data) => {
return {data: data || '', parent: foundCaptureItems[captureVar++].parent,};
});
}
}
static mapCaptureAndNameGroups(inputRegexSourceString) {
let REGEX_CAPTURE_GROUPS_ANALYZER = /((((?<!\\)|^)\((\?((<(?<name>\w+)))|(\?<=.*?\))|(\?<!.*?\))|(\?!.*?\))|(\?=.*?\)))?)|((?<!\\)\)(([*+?](\?)?))?|({\d+(,)?(\d+)?})))/gm;
return RegexContainer._findCaptureGroupsInRegexTemplate(REGEX_CAPTURE_GROUPS_ANALYZER, inputRegexSourceString);
}
static exec(re, input) {
let foundCaptureItems = RegexContainer.mapCaptureAndNameGroups(re.source);
let res = RegexContainer.execFull(re, input, foundCaptureItems);
return {captureItems: foundCaptureItems, results: res};
}
}
let answers = [];
let regex = [
{ re: "[ \\t]*?\\[\\[(?<inner>\\s*(?<core>\\w(.|\\s)*?)\\s*?)]]", label: "NESTED Regex"},
{ re: "(?<context>((\\w)(\\w|-)*))((?<separator>( - ))?(?<type>(-|\\w)+)?\\s*(?<opt>(\\{.*}))?)?[\\t ]*", label: "simpler regex" },
]
let input = "[[ context1 ]] [[ context2 - with-style { andOpts : {data: 'some info'} } ]]";
regex.forEach( (item) => {
let re = new RegExp(item.re, 'gm');
let result = RegexContainer.exec(re,input);
result.label = item.label;
answers.push(result);
});
answers.forEach((answer,index) => {
console.log('==========================================================');
console.log('==== Item ' + index + ' label: ' + answer.label + ' regex: ' + answer.captureItems[0].source );
console.log('==========================================================\n\n');
let scannedItems = answer.results;
scannedItems.forEach( (match) => {
let full = match[0];
let mstr = full.data;
let substr = input.substring(full.startPos, full.endPos);
if (mstr !== substr) {
console.log('error in the parsing if you get here');
return;
}
console.log('==== Checking ' + mstr);
for (let i=1; i<match.length; i++) {
let capture = match[i];
if (capture.groupName) {
console.log(' ' + capture.groupName + ': ' + "```" + input.substring(capture.startPos,capture.endPos) + "```");
}
}
console.log('');
});
});
Architecture
Take Regex Template and identify the capture groups it will generate. Save it off as an array of group items and nesting info to feed into the expanded exec() call.
use regex to find capturing starts, non-capturing elements, capture names and capture endings. Trap properly for the dreaded \( and \) items.
non-recursive inspection of capture items and their parents (using reference counting).
run the exec() with the capture group information pulled above.
use substring functions to extract data for each capture group
put everything into an array for each result found and send the array back.
Based on the ecma regular expression syntax I've written a parser respective an extension of the RegExp class which solves besides this problem (full indexed exec method) as well other limitations of the JavaScript RegExp implementation for example: Group based search & replace. You can test and download the implementation here (is as well available as NPM module).
The implementation works as follows (small example):
//Retrieve content and position of: opening-, closing tags and body content for: non-nested html-tags.
var pattern = '(<([^ >]+)[^>]*>)([^<]*)(<\\/\\2>)';
var str = '<html><code class="html plain">first</code><div class="content">second</div></html>';
var regex = new Regex(pattern, 'g');
var result = regex.exec(str);
console.log(5 === result.length);
console.log('<code class="html plain">first</code>'=== result[0]);
console.log('<code class="html plain">'=== result[1]);
console.log('first'=== result[3]);
console.log('</code>'=== result[4]);
console.log(5=== result.index.length);
console.log(6=== result.index[0]);
console.log(6=== result.index[1]);
console.log(31=== result.index[3]);
console.log(36=== result.index[4]);
I tried as well the implementation from #velop but the implementation seems buggy for example it does not handle backreferences correctly e.g. "/a(?: )bc(def(\1ghi)xyz)/g" - when adding paranthesis in front then the backreference \1 needs to be incremented accordingly (which is not the case in his implementation).
For global regex you want to match only fragments and iterate so first solution won't work. This is a 30 min solution based on indexOf and sums that work for this case:
https://codepen.io/cancerberoSgx/pen/qYwjjz?editors=0012#code-area
!function () {
const regex = /\/\*\*\*#\s*([^#]+)\s*(#\*\*\*\/)/gim
const exampleThatMatch = `
/***#
debug.print('hello editor, simpleNode kind is ' +
arg.simpleNode.getKindName())
#***/
const a = 1 //user
/***#
debug.print(arg.simpleNode.getParent().getKindName())
#***/
`
const text = exampleThatMatch
function exec(r, s) {
function indexOfGroup(match, n) {
var ix = match.index;
for (var i = 1; i < n; i++)
ix += match[i].length;
return ix;
}
let result
let lastMatchIndex = 0
const matches = []
while ((result = regex.exec(text))) {
const match = []
lastMatchIndex = text.indexOf(result[0], lastMatchIndex)
let relIndex = 0
for (let i = 1; i < result.length; i++) {
relIndex = text.indexOf(result[i], relIndex)
match.push({ value: result[i], start: relIndex, end: relIndex + result[i].length })
}
matches.push(match)
}
return matches
}
const groupsWithIndex = exec(regex, text)
console.log({RESULT: groupsWithIndex })
// now test - let's remove everything else but matched groups
let frag = '' , sep = '\n#######\n'
groupsWithIndex.forEach(match => match.forEach(group => {
frag += text.substring(group.start, group.end) + sep
}))
console.log('The following are only the matched groups usign the result and text.substring just to verify it works OK:', '\n'+sep)
console.log(frag)
}()
And just in case here is the typescript:
https://codepen.io/cancerberoSgx/pen/yjrXxx?editors=0012
|
Enjoy
I would like to extract initials from a string, like:
Name = FirstName LastName
Initials = FL
I can get the above result using this,
const initials = item
.FirstName
.charAt(0)
.toUpperCase() +
item
.LastName
.charAt(0)
.toUpperCase();
But now my requirements are changed as if name only consist of 1 word or more then 2, so in following cases how can I get initials as per my requirements,
FullName = FU
FirstName MiddleName LastName = FL
1stName 2ndName 3rdName 4thName 5thName = 15
How can I get above initials from a string in JS?
Also now I only have item.Name string as an input
Why no love for regex?
Updated to support unicode characters and use ES6 features
let name = 'ÇFoo Bar 1Name too ÉLong';
let rgx = new RegExp(/(\p{L}{1})\p{L}+/, 'gu');
let initials = [...name.matchAll(rgx)] || [];
initials = (
(initials.shift()?.[1] || '') + (initials.pop()?.[1] || '')
).toUpperCase();
console.log(initials);
You can use this shorthand js
"FirstName LastName".split(" ").map((n)=>n[0]).join(".");
To get only First name and Last name you can use this shorthand function
(fullname=>fullname.map((n, i)=>(i==0||i==fullname.length-1)&&n[0]).filter(n=>n).join(""))
("FirstName MiddleName OtherName LastName".split(" "));
Check the getInitials function below:
var getInitials = function (string) {
var names = string.split(' '),
initials = names[0].substring(0, 1).toUpperCase();
if (names.length > 1) {
initials += names[names.length - 1].substring(0, 1).toUpperCase();
}
return initials;
};
console.log(getInitials('FirstName LastName'));
console.log(getInitials('FirstName MiddleName LastName'));
console.log(getInitials('1stName 2ndName 3rdName 4thName 5thName'));
The functions split the input string by spaces:
names = string.split(' '),
Then get the first name, and get the first letter:
initials = names[0].substring(0, 1).toUpperCase();
If there are more then one name, it takes the first letter of the last name (the one in position names.length - 1):
if (names.length > 1) {
initials += names[names.length - 1].substring(0, 1).toUpperCase();
}
Get First and Last Initial: John Doe Smith => JS
name.match(/(\b\S)?/g).join("").match(/(^\S|\S$)?/g).join("").toUpperCase()
Get All Initials: "John Doe Smith" => "JDS"
name.match(/(\b\S)?/g).join("").toUpperCase()
Get First and Last except get First 2 in case there is only first. (OP's question)
John => JO and "John Doe Smith" => "JS"
name.match(/(^\S\S?|\b\S)?/g).join("").match(/(^\S|\S$)?/g).join("").toUpperCase()
International Version: "Störfried Würgekloß" => "SW"
name.match(/(^\S\S?|\s\S)?/g).map(v=>v.trim()).join("").match(/(^\S|\S$)?/g).join("").toLocaleUpperCase()
Note: If the name contains , or other non word characters, you might use /w instead of /S or sanitize it beforehand
Common Avatar Use-case
Just surprised that none of the answers put Array.reduce() to good use.
const getInitials = (fullName) => {
const allNames = fullName.trim().split(' ');
const initials = allNames.reduce((acc, curr, index) => {
if(index === 0 || index === allNames.length - 1){
acc = `${acc}${curr.charAt(0).toUpperCase()}`;
}
return acc;
}, '');
return initials;
}
Run the snippet below to check the initials for different use cases -
const testNames = [
'Albus Percival Wulfric Brian dumbledore', // AD
'Harry Potter', // HP
'Ron', // R
'', // <empty>
'Çigkofte With Érnie', // ÇÉ
'Hermione ', // H (Notice that there is a space after the name)
'Neville LongBottom ' // NL (space after name is trimmed)
]
const getInitials = (fullName) => {
const allNames = fullName.trim().split(' ');
const initials = allNames.reduce((acc, curr, index) => {
if(index === 0 || index === allNames.length - 1){
acc = `${acc}${curr.charAt(0).toUpperCase()}`;
}
return acc;
}, '');
return initials;
}
console.log(testNames.map(getInitials));
Note
This one is for a widely used case for displaying names in Avatars, where you wouldn't want first name initial to be repeated twice and want to restrict the initials to a max of 2 letters
You can use below one line logic:
"FirstName MiddleName LastName".split(" ").map((n,i,a)=> i === 0 || i+1 === a.length ? n[0] : null).join("");
There are some other answers which solve your query but are slightly complicated. Here's a more readable solution which covers most edge cases.
As your full name can have any number of words(middle names) in it, our best bet is to spit it into an array and get the initial characters from the first and last words in that array and return the letters together.
Also if your 'fullName' contains only one word, word at array[0] and array[array.length - 1] would be the same word, so we are handling that if the first if.
function nameToInitials(fullName) {
const namesArray = fullName.trim().split(' ');
if (namesArray.length === 1) return `${namesArray[0].charAt(0)}`;
else return `${namesArray[0].charAt(0)}${namesArray[namesArray.length - 1].charAt(0)}`;
}
Sample outputs :
> nameToInitials('Prince') // "P"
> nameToInitials('FirstName LastName') // "FL"
> nameToInitials('1stName 2ndName 3rdName 4thName 5thName')
// "15"
'Aniket Kumar Agrawal'.split(' ').map(x => x.charAt(0)).join('').substr(0, 2).toUpperCase()
let initial = username.match(/\b(\w)/g).join('')
You can do a function for that:
var name = 'Name';
function getInitials( name,delimeter ) {
if( name ) {
var array = name.split( delimeter );
switch ( array.length ) {
case 1:
return array[0].charAt(0).toUpperCase();
break;
default:
return array[0].charAt(0).toUpperCase() + array[ array.length -1 ].charAt(0).toUpperCase();
}
}
return false;
}
Fiddle: http://jsfiddle.net/5v3n2f95/1/
const getInitials = name => name
.replace(/[^A-Za-z0-9À-ÿ ]/ig, '') // taking care of accented characters as well
.replace(/ +/ig, ' ') // replace multiple spaces to one
.split(/ /) // break the name into parts
.reduce((acc, item) => acc + item[0], '') // assemble an abbreviation from the parts
.concat(name.substr(1)) // what if the name consist only one part
.concat(name) // what if the name is only one character
.substr(0, 2) // get the first two characters an initials
.toUpperCase(); // uppercase, but you can format it with CSS as well
console.log(getInitials('A'));
console.log(getInitials('Abcd'));
console.log(getInitials('Abcd Efgh'));
console.log(getInitials('Abcd Efgh Ijkl'));
console.log(getInitials('Abcd Efgh Ijkl Mnop'));
console.log(getInitials('Ábcd Éfgh Ijkl Mnop'));
console.log(getInitials('Ábcd - Éfgh Ijkl Mnop'));
console.log(getInitials('Ábcd / # . - , Éfgh Ijkl Mnop'));
+ efficient
+ no loops
+ simplified branching (ternary operator only)
+ handles no-space cases (prints 2 chars)
+ no array memory allocation (actually no array processing)
- requires trimmed string input
function getInitials(name) {
const hasTokens = name.indexOf(' ') !== -1
return name.substring(0, hasTokens ? 1 : 2) + (hasTokens ? name.charAt(name.lastIndexOf(' ') + 1) : '')
}
console.log(getInitials("A B"), 'AB')
console.log(getInitials("Abc Def"), 'AD')
console.log(getInitials("Abc Xyz"), 'AX')
console.log(getInitials("S Xyz"), 'SX')
console.log(getInitials("SXyz "), 'SX')
console.log(getInitials("T30"), 'T3')
Easier with map function:
var name = "First Last";
var initials = Array.prototype.map.call(name.split(" "), function(x){ return x.substring(0,1).toUpperCase();}).join('');
Similar but slightly neater version of #njmwas answer:
let name = 'Fred Smith';
let initials = name.split(' ').reduce((acc, subname) =>
acc + subname[0], '')
console.log(initials) // FS
Or, to include the abbreviation dots:
let name = 'Fred Smith';
let initials = name.split(' ').reduce((acc, subname) =>
acc + subname[0] + '.', '')
console.log(initials) // F.S.
This solution uses Array capabilities, Arrow function and ternary operator to achieve the goal in one line.
If name is single word, just take first two chars, but if more, then take 1st chars of first and last names.
(thanks omn for reminding single word name use case)
string.trim().split(' ').reduce((acc, cur, idx, arr) => acc + (arr.length > 1 ? (idx == 0 || idx == arr.length - 1 ? cur.substring(0, 1) : '') : cur.substring(0, 2)), '').toUpperCase()
To get the first name and last name initials, try using the function below.
const getInitials = string => {
const names = string.split(' ');
const initials = names.map(name => name.charAt(0).toUpperCase())
if (initials.length > 1) {
return `${initials[0]}${initials[initials.length - 1]}`;
} else {
return initials[0];
}
};
console.log(getInitials("1stName 2ndName 3rdName 4thName 5thName")); // 15
console.log(getInitials("FirstName MiddleName LastName")); // FL
WHAT HAPPENED: The function splits the incoming string, ignores any name between the first & last names and returns their initials. In the case a single name is entered, a single initial is returned. I hope this helps, cheers.
I needed this today to act as method in my React code. I was getting the user name from the state as props. After that I just passed my method inside my component's props.
getUserInitials() {
const fullName = this.props.user.name.split(' ');
const initials = fullName.shift().charAt(0) + fullName.pop().charAt(0);
return initials.toUpperCase();
}
function getInitials(name) {
return (
name
.match(/(?<=\s|^)\p{L}\p{Mn}*/gu)
?.filter((el, i, array) => i === 0 || i === array.length - 1)
.join("") || ""
);
}
console.log(getInitials('ÇFoo Bar 1Name too ÉLong'));
console.log(getInitials('Q̈lice Hwerty')); // Q is followed by U+0308 (Combining Diaeresis)
console.log(getInitials('A Foo'));
console.log(getInitials('Bob'));
Safari doesn't yet support lookbehinds in regexes (see caniuse), so if Safari support is needed, it can be rewritten this way:
function getInitials(name) {
return (
name
.match(/(\s|^)\p{L}\p{Mn}*/gu)
?.filter((el, i, array) => i === 0 || i === array.length - 1)
.map(el => el.trimStart())
.join("") || ""
);
}
const getInitials = name => {
let initials = '';
name.split(' ').map( subName => initials = initials + subName[0]);
return initials;
};
You can do something like this;
function initials(name){
//splits words to array
var nameArray = name.split(" ");
var initials = '';
//if it's a single word, return 1st and 2nd character
if(nameArray.length === 1) {
return nameArray[0].charAt(0) + "" +nameArray[0].charAt(1);
}else{
initials = nameArray[0].charAt(0);
}
//else it's more than one, concat the initials in a loop
//we've gotten the first word, get the initial of the last word
//first word
for (i = (nameArray.length - 1); i < nameArray.length; i++){
initials += nameArray[i].charAt(0);
}
//return capitalized initials
return initials.toUpperCase();
}
You can then use the function like so;
var fullname = 'badmos tobi';
initials(fullname); //returns BT
var surname = 'badmos';
initials(surname); //returns BA
var more = 'badmos gbenga mike wale';
initials(more); //returns BW;
I hope this helps.
var personName = "FirstName MiddleName LastName";
var userArray = personName.split(" ");
var initials = [];
if(userArray.length == 1){
initials.push(userArray[0][0].toUpperCase() + userArray[0][1]).toUpperCase();}
else if(userArray.length > 1){
initials.push(userArray[0][0].toUpperCase() + userArray[userArray.length-1][0].toUpperCase());}
console.log(initials);
This should work for majority of the cases including middle names and first name only (extension on #njmwas answer).
const initialArr = name.split(" ").map((n)=>n[0]);
const init = (initialArr.length > 1)? `${initialArr[0]}${initialArr[initialArr.length - 1]}` : initialArr[0];
const initials = init.toUpperCase();
Easy way using ES6 Destructering:
const getInitials = string =>
string
.split(' ')
.map(([firstLetter]) => firstLetter)
.filter((_, index, array) => index === 0 || index === array.length - 1)
.join('')
.toUpperCase();
THIS IS THE SIMPLE UTILITY METHOD THAT HELPS TO GET THE INITIALS OF THE NAME
BY SIMPLY PASSING THE NAME TO getInitials function
// eg getInitials("harry potter") ==> "HP"
const getInitials = (name) => {
var parts = name.split(' ')
var initials = ''
for (var i = 0; i < parts.length; i++) {
if (parts[i].length > 0 && parts[i] !== '') {
initials += parts[i][0]
}
}
return initials.toUpperCase();
}
Something more functional: D
const getInitials = (string) => {
const [firstname, lastname] = string.toUpperCase().split(' ');
const initials = firstname.substring(0, 1);
return lastname
? initials.concat(lastname.substring(0, 1))
: initials.concat(firstname.substring(1, 2));
};
console.log(getInitials('FirstName LastName')); // FL
console.log(getInitials('FirstName MiddleName LastName')); // FM
console.log(getInitials('FirstName')); // FI
var getInitials = function (string) {
var names = string.split(' '),
initials = names[0].substring(0, 1).toUpperCase()+'.';
if (names.length > 1) {
initials += names[names.length - 2].substring(0, 1).toUpperCase()+'.';
}
return initials=initials+names[names.length - 1].toUpperCase();
}
console.log(getInitials('Rama Krishna Narayan'));
var getInitials = function (string) {
var names = string.split(' '),
initials = names[0].substring(0, 1).toUpperCase()+'.';
if (names.length > 1) {
initials += names[names.length - 2].substring(0, 1).toUpperCase()+'.';
}
return initials=initials+names[names.length - 1].toUpperCase();
}
console.log(getInitials('Rama Krishna Narayan'));
A better way.
nameToInitials(name: string): string {
const portions = name.split(' ')
.map(val => val[0]);
return portions.slice(0, 2)
.reduce((a, b) => a + b, '').toUpperCase();
}
I am sorry if I misunderstand something, but most answers are so.... complicated.
My solution is (for international, actually hungarian names):
var regex = /[A-ZÍÉÁŰŐÚÖÜÓ]{1}/g
var monogram = 'Üveges Tóth Ödön'.match(regex).join('')
just updated Andrea's version:
var getInitials = function (string) {
var initials = "";
var names = string.split(' ');
for (n = 0; n < names.length; n++) {
initials += names[n].substring(0, 1).toUpperCase();
}
return initials;
};
if string includes LastName, just change names.length to names.length-1 to ignore lastname
Using some es6 functionality:
const testNameString = 'Hello World';
const testNameStringWeird = 'Hello darkness My - Óld Friend Nikolaus Koster-Walder ';
const getInitials = nameString =>{
const regexChar = /\D\w+/
return nameString
.trim() //remove trailing spaces
.split(' ') // splits on spaces
.filter(word => word.length > 0) // strip out double spaces
.filter(word => regexChar.test(word)) // strip out special characters
.map(word => word.substring(0, 1).toUpperCase()) // take first letter from each word and put into array
}
console.log('name:',testNameString,'\n initials:',getInitials(testNameString));
console.log('name:',testNameStringWeird,'\n initials:',getInitials(testNameStringWeird));