London | Nadika Zavodovska | Module-Tools | Sprint 3 | Implement-Shell-Tools#45
London | Nadika Zavodovska | Module-Tools | Sprint 3 | Implement-Shell-Tools#45nadika-zavodovska wants to merge 9 commits into
Conversation
ehwus
left a comment
There was a problem hiding this comment.
This is a great submission Nadika that betrays a good working knowledge of Node scripting as well as the functionality of the shell tools that you've been replicating. There are a few points that I've raised surrounding efficiency - as we're working with files, it's important that we're mindful of duplicating these into memory and the most efficient way to work with them will always be one line at a time, see this article for a guide on how to do that: https://www.geeksforgeeks.org/how-to-read-a-file-line-by-line-using-node-js/
Overall everything was very clear and readable and works when running it locally, keep it up!
| return line; | ||
| }); | ||
| // join lines back to the string and print them | ||
| console.log(arrayContentLines.join("\n")); |
There was a problem hiding this comment.
question: Do we need to join these back, or could we just print each line as we go?
There was a problem hiding this comment.
Thank you for the really useful comment, @ehwus Alex! You’re absolutely right, printing each line as we go is more efficient, especially when dealing with large files. I’ve updated my code to avoid unnecessary memory usage.
| //Get user inputs from command line | ||
| const filePaths = program.args; | ||
| const displayLineNumbers = program.opts().n; | ||
| const displayNonEmptyLineNumbers = program.opts().b; |
There was a problem hiding this comment.
praise: It's great to assign these options into variables that are more descriptive than the single letter flags, it makes the code much more readable
There was a problem hiding this comment.
Thank you for the positive feedback, @ehwus Alex. I'll continue to follow this approach in the future!
| } | ||
|
|
||
| // Handling multiply file processing using Promise.all and map() | ||
| await Promise.all(arrayFilePaths.map(filePath => displayFileContents(filePath, displayLineNumbers, displayNonEmptyLineNumbers))); |
There was a problem hiding this comment.
question: Why is map useful here over forEach?
There was a problem hiding this comment.
Thank you, @ehwus Alex, great question. map is used because it returns an array of promises, which is needed for Promise.all to wait for all tasks to complete. forEach doesn't return anything, so it wouldn't work with Promise.all.
| .parse(process.argv) | ||
|
|
||
| // Function to print file contents with the flags | ||
| async function displayFileContents(filePath, displayLineNumbers = false, displayNonEmptyLineNumbers = false) { |
There was a problem hiding this comment.
issue: This function works and is readable, but we're loading the same data into memory multiple times in different ways. Can you think of any ways that this could be made more efficient?
There was a problem hiding this comment.
@ehwus Alex, thank you for the really useful feedback! Yes, there is a more efficient way, I can use the readline module. I've updated the code to use readline to read the file line by line, which helps prevent loading the entire file into memory at once.
| // Importing the 'program' object from 'commander' to help build command-line interfaces, handle user inputs. | ||
| import { program } from "commander"; | ||
| // Importing the 'chalk' module to add colors and styles to text in the console | ||
| import chalk from "chalk"; |
| // Array stores the names of the files and directories | ||
| let displayFiles = []; | ||
| // Ensure '.', '..' are on the top of list when we list hidden files | ||
| if (showHiddenFiles) { |
There was a problem hiding this comment.
praise: This is a clever way of adding the dot directories
| ); | ||
| }); | ||
| // Array with style directory in bold and blue | ||
| const styledAllFiles = await Promise.all(allFiles); |
| // Split content into array by lines | ||
| let arrayLines = content.split("\n"); | ||
| // If the last line is empty - remove it | ||
| while (arrayLines.length > 0 && arrayLines[arrayLines.length - 1].trim() === "") { |
There was a problem hiding this comment.
question: Do we really need a while loop to achieve the result that we're looking for in the comment?
There was a problem hiding this comment.
You are right, we can simply filter out empty lines at the end by using arrayLines.filter(line => line.trim() !== "") without needing the while loop. I have updated the code. Thank you, @ehwus Alex!
| } | ||
|
|
||
| // Get the number of lines | ||
| const countNumLines = arrayLines.length; |
There was a problem hiding this comment.
nitpick: Do we always need to know the results of each of these?
There was a problem hiding this comment.
You're right, it's usually better to keep the code concise and avoid unnecessary variables. In this case, I used a variable because I needed the same value in two places, so it helped avoid repeating the calculation. But I agree, that keeping the code clean and minimal is a good practice.
| .parse(process.argv); | ||
|
|
||
| // Function to count bytes, words and lines in a file | ||
| async function countDisplayNumBytesWordsLines(filePath, numLines = false, numWords = false, numBytes = false) { |
There was a problem hiding this comment.
praise: This function encapsculates the core logic well
|
@ehwus Alex, thank you for your insightful and encouraging feedback, and the helpful link! I read through the article, and it was really useful for learning how to work with files one line at a time and manage memory more efficiently. I appreciate your support in writing cleaner and more maintainable code. |
Hello,
I've done all the tasks for the Implement-Shell-Tools.
Self checklist
Thank you.