I was wondering how to trim a file name in JS to show "..." or any appendix for that matter after a certain number of characters, the most efficient way to handle all possible test cases.
Rules
- Show the actual file extension and not the last character after splitting the string name with "."
- The function should take the input file name (string), the number of characters to trim (integer) and appendix (string) as the parameter.
- By efficient, I mean I expect to write fewer lines of code and handle all possible edge cases.
Sample Inputs
- myAwesomeFile.min.css
- my Awesome File.tar.gz
- file.png
Expected output (say I want to trim after 5 characters)
- myAwe....min.css
- my Aw....tar.gz
- file.png
Editing the question to show my attempt
function trimFileName(str, noOfChars, appendix) {
let nameArray = str.split(".");
let fileType = `.${nameArray.pop()}`;
let fileName = nameArray.join(" ");
if (fileName.length >= noOfChars) {
fileName = fileName.substr(0, noOfChars) + appendix;
};
return (fileName + fileType);
}
console.log(trimFileName("myAwesomeFile.min.css", 5, "..."));
console.log(trimFileName("my Awesome File.tar.gz", 5, "..."));
console.log(trimFileName("file.png", 5, "..."));
I was wondering how to trim a file name in JS to show "..." or any appendix for that matter after a certain number of characters, the most efficient way to handle all possible test cases.
Rules
- Show the actual file extension and not the last character after splitting the string name with "."
- The function should take the input file name (string), the number of characters to trim (integer) and appendix (string) as the parameter.
- By efficient, I mean I expect to write fewer lines of code and handle all possible edge cases.
Sample Inputs
- myAwesomeFile.min.css
- my Awesome File.tar.gz
- file.png
Expected output (say I want to trim after 5 characters)
- myAwe....min.css
- my Aw....tar.gz
- file.png
Editing the question to show my attempt
function trimFileName(str, noOfChars, appendix) {
let nameArray = str.split(".");
let fileType = `.${nameArray.pop()}`;
let fileName = nameArray.join(" ");
if (fileName.length >= noOfChars) {
fileName = fileName.substr(0, noOfChars) + appendix;
};
return (fileName + fileType);
}
console.log(trimFileName("myAwesomeFile.min.css", 5, "..."));
console.log(trimFileName("my Awesome File.tar.gz", 5, "..."));
console.log(trimFileName("file.png", 5, "..."));
Edit #2: Feel free to go ahead and edit the question if you think it's not the standard expectation and add more edge cases to the sample inputs and expected outputs.
Edit #3: Added a few more details to the question after the new ments. I know my attempt doesn't fulfill my expected outputs (and I am unsure whether the output I have listed above is a standard expectation or not).
Edit #4 (Final): Removed the rule of not breaking a word in the middle after a continuous backlash in the ments and changed the rules to cater to more realistic and practical use cases.
Share Improve this question edited Feb 2, 2023 at 4:43 Penny Liu 17.6k5 gold badges86 silver badges108 bronze badges asked Oct 30, 2019 at 7:01 sibasishmsibasishm 4427 silver badges26 bronze badges 15- 2 You have a lot of cases and not even a single lick of an attempt to acplish one of them? Not to mention you're looking for the most efficient way, when you haven't showed us what you tried yet. – Potato Salad Commented Oct 30, 2019 at 7:06
- 2 This is really unclear what you're tryin to do... the expected output is unconsistent and does not match the description of "I want to trim after 5 chars" at all. Please give concrete examples and showus what you've tried – Gibor Commented Oct 30, 2019 at 7:09
- 3 By what metric do you measure "most efficient"? – Ingo Bürk Commented Oct 30, 2019 at 7:09
-
4
What code have you tried? This is really just rote string processing. There aren't any magic shortcuts. Decide what algorithm you want to use for deciding where you want to break into a
...
and write the code to implement that algorithm. Also, most trim operations like this give themselves a max number of characters they will allow to remain unbroken because typically, the whole point of doing this is to fit some abbreviated representation of the string into a pre-defined space. So, in your #2 case, if you don't break it at all, it kind of defeats the typical purpose. – jfriend00 Commented Oct 30, 2019 at 7:13 -
3
I'm still not clear on what the rules of the contraction are. What is considered "a word" for the algorithm to avoid breaking into parts? What happens if you do stop within a word - do you show the entire thing or hide it? Does it depend on the position? It seems like you want to show the entire word if it's the first word in a name, but hide it if it's the second one. But what happens if it's the third one?
my.w.another_word
? Also, do you want actual file extension checking or only close enough (.tar.gz
is treated as if it's.tar
? – VLAZ Commented Oct 30, 2019 at 8:00
5 Answers
Reset to default 4 +25If we treat the dot character .
as a separator for file extensions, what you ask for can be solved with a single line of JavaScript:
name.replace(new RegExp('(^[^\\.]{' + chars + '})[^\\.]+'), '$1' + subst);
Demo code in the following snippet:
function f(name, chars, subst) {
return name.replace(
new RegExp('(^[^\\.]{' + chars + '})[^\\.]+'), '$1' + subst);
}
test('myAwesomeFile.min.css', 5, '...', 'myAwe....min.css');
test('my Awesome File.tar.gz', 5, '...', 'my Aw....tar.gz');
test('file.png', 5, '...', 'file.png');
function test(filename, length, subst, expected) {
let actual = f(filename, length, subst);
console.log(actual, actual === expected ? 'OK' : 'expected: ' + expected);
}
On Windows, AFAIK, the file extension is only what follows the last dot. Thus, technically, the file extension of "myAwesomeFile.min.css"
is just css
, and the file extension of "my Awesome File.tar.gz"
is just gz
.
In this case, what you ask for can still be solved with one line of JavaScript:
name.replace(new RegExp('(^.{' + chars + '}).+(\\.[^\\.]*$)'), '$1' + subst + '$2');
Demo code in the following snippet:
function f(name, chars, subst) {
return name.replace(
new RegExp('(^.{' + chars + '}).+(\\.[^\\.]*$)'), '$1' + subst + '$2');
}
test('myAwesomeFile.min.css', 5, '...', 'myAwe....css');
test('my Awesome File.tar.gz', 5, '...', 'my Aw....gz');
test('file.png', 5, '...', 'file.png');
function test(filename, length, subst, expected) {
let actual = f(filename, length, subst);
console.log(actual, actual === expected ? 'OK' : 'expected: ' + expected);
}
If you really want to allow edge cases with specific multiple extensions, you probably need to define a prehensive list of all allowed multiple extensions to know how to deal with cases like "my.awesome.file.min.css"
. You would need to provide a list of all cases you want to include before it would be possible to determine how efficient any solution could be.
It is really hard to account for all extensions (including edge cases). See this list for example for mon extensions: https://www.puterhope./issues/ch001789.htm. Event with that many extensions, the list is exhaustive of all extensions.
Your function is OK but to account for more cases it could be re-written to this:
function trimFileName(filename, limit = 5, spacer = '.') {
const split = filename.indexOf(".");
const name = filename.substring(0, split);
const ext = filename.substring(split);
let trimName = name.substring(0, limit);
if (name.length > trimName.length)
trimName = trimName.padEnd(limit + 3, spacer);
return trimName + ext;
}
console.log(trimFileName("myAwesomeFile.min.css"));
console.log(trimFileName("my Awesome File.tar.gz"));
console.log(trimFileName("file.png"));
Below is a pretty simple approach to achieve shortening in the fashion you desire. Comments are in the code, but let me know if anything needs additional explanation:
//A simple object to hold some configs about how we want to shorten the file names
const config = {
charsBeforeTrim: 5,
seperator: '.',
replacementText: '....'
};
//Given file names to shorten
const files = ['myAwesomeFile.min.css', 'my Awesome File.tar.gz', 'file.png'];
//Function to do the actual file name shortening
const shorten = s =>
s.length > config.charsBeforeTrim ? `${s.substring(0, config.charsBeforeTrim)}${config.replacementText}` : s;
//Function to generate a short file name with extension(s)
const generateShortName = (file, config) => {
//ES6 Array destructuring makes it easy to get the file name in a unique variable while keeping the remaining elements (the extensions) in their own array:
const [name, ...extensions] = file.split(config.seperator);
//Simply append all remaining extension chunks to the shortName
return extensions.reduce((accum, extension) => {
accum += `${config.seperator}${extension}`;
return accum;
}, shorten(name));
};
//Demonstrate usage
const shortFileNames = files.map(file => generateShortName(file, config));
console.log(shortFileNames);
const parse = (filename, extIdx = filename.lastIndexOf('.')) => ({
name: filename.substring(0, extIdx),
extension: filename.substring(extIdx + 1),
})
const trimFileName = (
filename, size = 5, fill = '...',
file = parse(filename),
head = file.name.substring(0, size)
) => file.name.length >= size ? `${head}${fill}${file.extension}` : filename
/* - - - - - - - - - - - - - - - - - - - - - - - - - */
;[
'myAwesomeFile.min.css',
'my.Awesome.File.min.css',
'my Awesome File.tar.gz',
'file.png',
].forEach(f => console.log(trimFileName(f)))
You can fairly straightforwardly pull the your extension condition (easily replaced with a list of valid extensions) and regex pull the last part. Then you just add a check on the filename (starting at the beginning of filename) to trim the result.
const trim = (string, x) => {
// We assume that up to last two . delimited substrings of length are the extension
const extensionRegex = /(?:(\.[a-zA-Z0-9]+){0,2})$/g;
const { index } = extensionRegex.exec(string);
// No point in trimming since result string would be longer than input
if (index + 2 < x) {
return string;
}
return string.substr(0, x) + ".." + string.substr(index);
};
/* Assert that we keep the extension */
console.log("cat.tar.gz", trim("cat.tar.gz", 100) == "cat.tar.gz");
console.log("cat.zip", trim("cat.zip", 100) == "cat.zip");
/* Assert that we keep x characters before trim */
console.log("1234567890cat.tar.gz",!trim("1234567890cat.tar.gz",10).includes("cat"));
console.log("1234567890cat.zip", !trim("1234567890cat.zip", 10).includes("cat"));