Skip to content

London| Elhadj Abdoul Diallo| Module-Tools | WEEK3 - Implement-shell-tools#25

Open
eediallo wants to merge 64 commits into
CodeYourFuture:mainfrom
eediallo:implement-shell-tools
Open

London| Elhadj Abdoul Diallo| Module-Tools | WEEK3 - Implement-shell-tools#25
eediallo wants to merge 64 commits into
CodeYourFuture:mainfrom
eediallo:implement-shell-tools

Conversation

@eediallo

@eediallo eediallo commented Mar 5, 2025

Copy link
Copy Markdown

Learners, PR Template

Self checklist

  • I have committed my files one by one, on purpose, and for a reason
  • I have titled my PR with COHORT_NAME | FIRST_NAME LAST_NAME | REPO_NAME | WEEK
  • I have tested my changes
  • My changes follow the style guide
  • My changes meet the requirements of this task

[Elhadj Abdoul Diallo] added 30 commits March 3, 2025 11:36
@eediallo eediallo added the Needs Review Trainee to add when requesting review. PRs without this label will not be reviewed. label Mar 5, 2025
@ehwus ehwus added Review in progress This review is currently being reviewed. This label will be replaced by "Reviewed" soon. and removed Needs Review Trainee to add when requesting review. PRs without this label will not be reviewed. labels Mar 19, 2025

@ehwus ehwus left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cat

You've produced a readable and working replica of the cat shell tool here, and it shows a solid understanding of node fundamentals and using commander to build scripts. There are a few tweaks that we could make to get this to be as efficient as cat is, and I would focus on:

  • Learning about operating on files in a memory efficient manner
  • Why and when to await the response of promises
  • JavaScript array iteration - which to use when and why

Overall though this is a really solid effort and it was great to see you reach such parity with the cat tool with your own code.

ls

Once again this is a good implementation of ls and the code is clear and readable. There are a few flags for efficiency and clarity, but overall a very good effort.

wc

Once again this largely replicates wc well barring a few little logic tweaks that I have raised, but parts of the code feel complicated to follow and we have also fallen into some of the previously discussed patterns surrounding reading from the filesystem. It feels like it doesn't have the readability of the other two scripts, so I'm curious what the difference was when you wrote it - did you find it a harder problem to solve?

Overall

This is a great demonstration of your knowledge of shell toolings and how to replicate this functionality well in Node, which you were able to do successfully. Keep tradeoffs in mind, particularly when working with the file system, and make sure that you have a good understanding of when to use each array function and when it's useful to await promises.

Comment thread implement-shell-tools/cat/cat.js Outdated
.argument("<paths...>", "The file paths to process")
.parse(process.argv);

let args = program.args;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Are we ever reassigning this let?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are not. It makes sense to use the const keyword here.

Comment thread implement-shell-tools/cat/cat.js Outdated
.parse(process.argv);

let args = program.args;
//console.log(args)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: It's good practice to leave these debugging comments out of code, but good to see where you were checking your inputs as you went along

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. going to remove it. Thanks

Comment thread implement-shell-tools/cat/cat.js Outdated
let args = program.args;
//console.log(args)

const nOption = program.opts().number;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I appreciate here that cat uses these single letters to denote functionality, but you could make your own life easier here and name them something that's more clear for your own code, i.e. displayLineNumber

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do. Thanks. I was actually struggling to find meaning names for these options.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton


async function readAndPrintFileContent(path) {
try {
const content = await fs.readFile(path, { encoding: "utf-8" });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: What does this do to the file from the filesystem? Could we do this more memory efficiently?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's reading the content of the file(path) provided through the CLI asyncronously which means it won't prevent other tasks from executing. Could we do this more memory efficiently? I am not 100% sure but I know if I was reading a large file, I would have used Stream to read it in chuncks instead of loading it once.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're along the right lines here, text files might be absolutely massive (e.g. logs) so making sure that our programs operate on them efficiently is really important, especially as we're processing line by line anyway

Comment thread implement-shell-tools/cat/cat.js Outdated
}

function extractLinesFromContent(content) {
const lines = content.split("\n");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Why could this line of code make the situation that we've introduced on line 22 worse?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure. Could you please advice what would be the best practice here?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we've loaded the entire file into memory on line 22, what are we doing here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are processing it once.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're copying the entirety of the file into memory again in an array, so we've doubled our problem

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I see. Googling to see how to avoid this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have refactored it by removing the extractLinesContent function and extracting lines directly in readAndPrintFileContent function. Would this solve the problem?

* @returns {number[]|number} - An array of sums for each column of numbers if there are multiple files,
* or 0 if there is only one file.
*/
function aggregateFileData(fileCounts) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Please could you talk me through this function line by line?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure the aggregatedFilesData is an array with three possible values: sum of lines, chars or words. The following code:

 if (aggregatedFilesData !== 0 && lineOption && !(wordOption || charOption)) {
    console.log(`${aggregatedFilesData[0]} total`);
    return;
  }

whether aggregatedFilesData is not 0 and line option is provided but not the rest of the options(chars and words).if it is true, it means only sum of lines is available on aggregatedFilesData array therefore the sum is displayed with total in front of it.

it is the same for chars option in the following code:

if (aggregatedFilesData !== 0 && charOption && !(wordOption || lineOption)) {
    console.log(`${aggregatedFilesData[0]} total`);
    return;
  }

Here:

if (
    aggregatedFilesData !== 0 &&
    ((lineOption && charOption) || (lineOption && wordOption))
  ) {
    console.log(`${aggregatedFilesData[0]} ${aggregatedFilesData[1]} total`);
    return;
  }

This code checks if two options are provided, it could be line option and char option or line and word option. if it is true, I know the aggregatedFilesData array contains two values which are then displayed alongside the total string.

The return keyword in each statement ensure that the execution does not continue.

if the bove conditions are false: the sum of each option is display if aggregatedFilesData is not 0, hence the code below: aggregatedFilesData !== 0
? console.log(
${aggregatedFilesData[0]} ${aggregatedFilesData[1]} ${aggregatedFilesData[2]} total
)
: null;

const wordOption = program.opts().word;

async function countLinesWordsCharsInFile(path) {
const content = await fs.readFile(path, { encoding: "utf-8" });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: What are the tradeoffs of reading files in this way?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the file was large, that would caused high memory consumption and possible out of memory errors.

Comment thread implement-shell-tools/wc/wc.js Outdated
async function countLinesWordsCharsInFile(path) {
const content = await fs.readFile(path, { encoding: "utf-8" });

const lines = removeEndEmptyLine(content.split("\n"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: These are clever ways of getting the counts, but do we always need to do all of this work?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe not. may I could have write the code instead of creating a separate function for it.

@eediallo eediallo Mar 19, 2025

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have refactore this code too to avoid copying the entire array inot memory again.

const numberOfWords = words.length;
const numberOFChars = chars.length;

if (lineOption) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: What if I'd like to have the lines and the chars?

Screenshot 2025-03-19 at 08 46 07

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I've not implemented this one. I am going to add the functionality

}

async function processFilesAndDisplayCounts() {
const fileCounts = await Promise.all(args.map(countLinesWordsCharsInFile));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: awaiting and using Promise.all with a map here makes a lot of sense and makes sure we can process as many files as are needed, great stuff

@ehwus ehwus added Reviewed Volunteer to add when completing a review with trainee action still to take. and removed Review in progress This review is currently being reviewed. This label will be replaced by "Reviewed" soon. labels Mar 19, 2025
@ehwus ehwus added Complete Volunteer to add when work is complete and all review comments have been addressed. and removed Reviewed Volunteer to add when completing a review with trainee action still to take. labels Mar 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Complete Volunteer to add when work is complete and all review comments have been addressed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants