Finished
4141
4242const ARGS = process.argv;
4343
44//
45// This is only a SKELETON file for the 'Grep' exercise. It's been provided as a
46// convenience to get you started writing code faster.
47//
48// This file should *not* export a function. Use ARGS to determine what to grep
49// and use console.log(output) to write to the standard output.
44// Parse command line arguments
45let flags = [];
46let pattern = '';
47let files = [];
48let flagParsing = true;
49
50for (let i = 2; i < ARGS.length; i++) {
51 const arg = ARGS[i];
52
53 // Edge Case: Handle empty argument
54 if (!arg) continue;
55
56 if (flagParsing && arg.startsWith('-')) {
57 // Edge Case: Handle combined flags like -niv
58 const flagChars = arg.slice(1);
59 for (const char of flagChars) {
60 if (VALID_OPTIONS.includes(char)) {
61 flags.push(char);
62 } else {
63 console.error(`Invalid option: ${char}`);
64 process.exit(1);
65 }
66 }
67 } else {
68 flagParsing = false;
69 if (!pattern) {
70 pattern = arg;
71 } else {
72 files.push(arg);
73 }
74 }
75}
76
77// Edge Case: No pattern provided
78if (!pattern) {
79 console.error('Pattern is required');
80 process.exit(1);
81}
82
83// Edge Case: No files provided
84if (files.length === 0) {
85 console.error('At least one file is required');
86 process.exit(1);
87}
88
89const hasLineNumbers = flags.includes('n');
90const hasFileNameOnly = flags.includes('l');
91const ignoreCase = flags.includes('i');
92const invertMatch = flags.includes('v');
93const matchEntireLine = flags.includes('x');
94
95// Prepare pattern for comparison
96let searchPattern = pattern;
97if (ignoreCase) {
98 searchPattern = pattern.toLowerCase();
99}
100
101// Store results
102const fileMatches = new Set();
103const outputLines = [];
104
105// Process each file
106for (const file of files) {
107 // Edge Case: File doesn't exist
108 if (!fs.existsSync(file)) {
109 console.error(`File not found: ${file}`);
110 process.exit(1);
111 }
112
113 try {
114 const lines = readLines(file);
115 let fileHasMatch = false;
116
117 // Edge Case: Empty file
118 if (lines.length === 0) {
119 continue;
120 }
121
122 for (let i = 0; i < lines.length; i++) {
123 const line = lines[i];
124 const lineNumber = i + 1;
125
126 // Prepare line for comparison
127 let compareLine = line;
128 if (ignoreCase) {
129 compareLine = line.toLowerCase();
130 }
131
132 // Check for match
133 let isMatch;
134 if (matchEntireLine) {
135 isMatch = compareLine === searchPattern;
136 } else {
137 isMatch = compareLine.includes(searchPattern);
138 }
139
140 // Apply invert flag
141 if (invertMatch) {
142 isMatch = !isMatch;
143 }
144
145 if (isMatch) {
146 fileHasMatch = true;
147
148 // If we only want file names, we can break early
149 if (hasFileNameOnly) {
150 fileMatches.add(file);
151 break;
152 }
153
154 // Build output line
155 let outputLine = '';
156
157 // Add file name if multiple files
158 if (files.length > 1) {
159 outputLine += `${file}:`;
160 }
161
162 // Add line number if requested
163 if (hasLineNumbers) {
164 outputLine += `${lineNumber}:`;
165 }
166
167 outputLine += line;
168 outputLines.push(outputLine);
169 }
170 }
171
172 // Edge Case: Track file matches for -l flag
173 if (fileHasMatch && hasFileNameOnly) {
174 fileMatches.add(file);
175 }
176 } catch (error) {
177 // Edge Case: File cannot be read
178 console.error(`Error reading file ${file}: ${error.message}`);
179 process.exit(1);
180 }
181}
182
183// Output results
184if (hasFileNameOnly) {
185 // Edge Case: No matching files
186 const sortedFiles = Array.from(fileMatches).sort();
187 if (sortedFiles.length > 0) {
188 console.log(sortedFiles.join('\n'));
189 }
190} else {
191 // Edge Case: No matching lines
192 if (outputLines.length > 0) {
193 console.log(outputLines.join('\n'));
194 }
195}
196
197// Handled Edge Cases: empty argument, combined flags, no pattern provided, no files provided, file doesn't exist, empty file, file cannot be read, no matching files, no matching lines
Test NameStatus
One file, one match, no flags
Pass
One file, one match, print line numbers flag
Pass
One file, one match, case-insensitive flag
Pass
One file, one match, print file names flag
Pass
One file, one match, match entire lines flag
Pass
One file, one match, multiple flags
Pass
One file, several matches, no flags
Pass
One file, several matches, print line numbers flag
Pass
One file, several matches, match entire lines flag
Pass
One file, several matches, case-insensitive flag
Pass
One file, several matches, inverted flag
Pass
One file, no matches, various flags
Pass
One file, one match, file flag takes precedence over line flag
Pass
One file, several matches, inverted and match entire lines flags
Pass
Multiple files, one match, no flags
Pass
Multiple files, several matches, no flags
Pass
Multiple files, several matches, print line numbers flag
Pass
Multiple files, one match, print file names flag
Pass
Multiple files, several matches, case-insensitive flag
Pass
Multiple files, several matches, inverted flag
Pass
Multiple files, one match, match entire lines flag
Pass
Multiple files, one match, multiple flags
Pass
Multiple files, no matches, various flags
Pass
Multiple files, several matches, file flag takes precedence over line number flag
Pass
Multiple files, several matches, inverted and match entire lines flags
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.