Best approach on how read from terminal? Kattis counting stars - javascript

Im trying to dig into this kattis.com challenge called counting stars using NodeJs. If you see the input example, it always start with a line of two integers, the first one represents the number of rows, the second one represents the numbers of columns per row, there are a lot of examples with C and Java, but I want to solve this with JS (Nodejs). How I can read from the terminal and iterate line by line, I tried with the next code
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
var lines = [];
rl.on('line', line => {
lines.push(line);
console.log(lines);
});
But for some reason, if I input this sample
3 10
#-########
----------
#-########
It only show me the first 3 lines, also tried with this line while (line = readline()) {}, but also not working because of readline isn't a function
In Java they use a class of Scanner(System.in);, and probably that class make things easier

You're on the right track. The missing piece is adding code to distinguish test cases. The easiest option might be to slurp all of the lines into an array, listen to "close" on the readline and then process everything synchronously in one go, chunking out tests based on the ^\d+ \d+$ pattern.
However, this approach is memory-unfriendly, so I'd prefer to only consume one test case at a time, solve it in isolation, then free the memory:
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on("line", line => {
if (/^\d+ \d+$/g.test(line)) {
if (lines.length) {
solve(lines, id++);
lines.length = 0;
}
}
else {
lines.push(line);
}
})
.on("close", () => solve(lines, id));
let id = 1;
const lines = [];
const solve = (grid, id) => {
let count = 0;
// your algorithm here
console.log(`Case ${id}: ${count}`);
};
See also:
Getting input in Kattis challenges - readline js
Kattis - readline (deleted thread--10k+ rep to view)
How to line split in Kattis problem-solving?
using readline stdin in node.js
Reading multiple lines in node.js

Related

Sibice challenge open Kattis JavaScript not working

I am learning JavaScript, and my friend recommended open Kattis to solve tasks. (I see now that it might not be the best for JS).
Anyway, I am doing this challenge Sibice where you are checking if matches will fit a box. Read the challenge linked for the context.
I am solving the problem, but I am not passing any of the tests Kattis has for the problem. I use readline to take input, since you need to take the input from terminal in open Kattis.
Attached is my code. Sorry if it's messy (will take tips on that too haha), do you have any ideas? Also my first question, so I apologize if I haven't submitted it perfectly!
This is my code, and when I run it, it works fine. But Kattis is not accepting it.
Sample input and output
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question(
"Enter the number of matches and the width and height of the box, separated by spaces: ",
input => {
const [numMatches, width, height] = input
.split(" ")
.map(Number);
const length = Math.sqrt(width * width + height * height);
const matchLengths = [];
const matchArray = () => {
return new Promise(resolve => {
rl.question("match length?", matchLength => {
matchLengths.push(matchLength);
resolve();
});
});
};
const askQuestion = async numMatches => {
for (i = 0; i < numMatches; i++) {
await matchArray();
}
rl.close();
};
askQuestion(numMatches).then(() => {
matchLengths.forEach(element => {
console.log(element <= length ? "DA" : "NE");
});
});
},
);
Avoid rl.question, which prints a prompt to standard out, the stream that Kattis is looking at to determine whether your program is correct. Your program should be totally silent except for printing the exact expected output, nothing more or less. Stifle your UX instincts and focus on the algorithm.
I usually recommend the following approach for solving Kattis problems in JS:
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.once("line", line => {
// process preamble
rl.on("line", line => {
// process line
});
rl.on("close", () => {
// print result of computation that requires all lines
});
});
However, for this problem, each test case can be computed and printed after each line, so you can skip the close listener and array:
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.once("line", line => {
const [n, w, h] = line.split(" ").map(Number);
const length = Math.sqrt(w * w + h * h);
rl.on("line", line => console.log(line > length ? "NE" : "DA"));
});
If you really want to use promises, this was also accepted:
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const read = () => new Promise(r => rl.once("line", line => r(line)));
(async () => {
const [n, w, h] = (await read()).split(" ").map(Number);
const length = Math.sqrt(w * w + h * h);
for (let i = 0; i < n; i++) {
console.log((await read()) > length ? "NE" : "DA");
}
})();
That said, I vaguely recall running into issues with promisification on other Kattis problems, and promises don't seem to offer much of an elegance boost here anyway, so I'd stick to plain callbacks.
As an aside, your for (i = 0; i < numMatches; i++) { should use let i to avoid introducing a globally-scoped variable that can easily lead to bugs.
See also:
Getting input in Kattis challenges - readline js
How to line split in Kattis problem-solving?
Regarding code formatting, the standard is Prettier, which you can install locally or use in their online playground. I already applied it to your post.

#upstash/redis scan command

Using #upstash/redis node client (https://www.npmjs.com/package/#upstash/redis) im trying to run the scan command (that should be supported, according to docs), but im not able to set the options:
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL,
token: process.env.UPSTASH_REDIS_RO_REST_TOKEN,
});
const result = await redis.scan(0);
This way it works, but when i try to add MATCH or COUNT (it says it is ScanCommandOptions type) i get no results. I tried several ways
const result = await redis.scan(0, MATCH, 'title*');
const result = await redis.scan(0, (MATCH, 'title*'));
const result = await redis.scan(0, {MATCH:'title*'});
but it looks these are not the correct way, i cant find any example, any suggestion?
Thank you
scan(cursor: number, opts: {
match?: string
count?: number
type?: string
})
For more details, see this Github Issue

Conflicting benchmark tests while looping over nested arrays

Info
I am trying to create a effective way to loop over an array with nested objects/arrays as the function looping the data will run frequently and filter values based on if they are matching records in another array containing objects.
The data I am working with has the following types.
type WatchedShows = {
showId: number;
season: number;
episode: number;
}[];
type Shows = {
id: number;
seasons: {
season: {
season_number: number;
episodes: {
episode_number: number;
}[];
};
}[];
}[];
The data in WatchedShows is in my own database so I can control how it is sent to the frontend but, the data in Shows comes from an external API.
What I am trying to do is filter all episodes in Shows that matches the data in WatchedShows and also filter seasons and whole shows if everything is marked as watched.
Problem
Currently I have 3 different solutions but, the first was 3 nested loops and became slow quickly with some data so realistically I have 2 solutions. I also need to return the data in the format of Shows
I have now tried to run them through a benchmark and tested a bit with timers and checking how many iterations each of them have. Looking at the result from the test I did my self there is a clear winner as one has a run time of ~5ms and 42637 iterations while the other has a run time of ~15ms and 884052 iterations.
I then tried to run it through JSBench.me and when I do that its the opposite solution that comes out as the winner with the website saying its 90% faster. How can that happen when that solution has 884052 iterations and the other 42637 iterations? Is it my solutions that are badly optimized or are there something else I am missing? Tips on improving the solutions would be appreciated.
Both test were done with the same code generated dataset with about 20 000 episode records spread over 26 shows, 20 seasons in each and 40 episodes in each season. The code that generates the dataset can be seen on the benchmark site if needed. The benchmark can be seen here
Code
The solution with 884052 iterations and run time of ~15ms looks like this
const newShowObject = {};
for (const show of shows) {
newShowObject[show.id] = { ...show };
}
for (const show of watchedShows) {
for (const season of newShowObject[show.showId].seasons) {
if (season.season_number !== show.season) {
continue;
}
season.episodes = season.episodes.filter(
(episode) => episode.episode !== show.episode
);
}
newShowObject[show.showId].seasons = newShowObject[show.showId].seasons.filter(
(season) => season.episodes.length > 0
);
}
const unwatchedShows = [...Object.values(newShowObject)].filter(
(show) => show.seasons.length > 0
);
The solution with 42637 iterations and run time of ~5ms looks like this
const newShowObject = {};
for (const show of shows) {
newShowObject[show.id] = { ...show };
const newSeasons = {};
for (const seasons of show.seasons) {
newSeasons[seasons.season_number] = { ...seasons };
const newEpisodes = {};
for (const episodes of seasons.episodes) {
newEpisodes[episodes.episode] = { ...episodes };
}
newSeasons[seasons.season_number].episodes = { ...newEpisodes };
}
newShowObject[show.id].seasons = { ...newSeasons };
}
for (const show of watchedShows) {
delete newShowObject[show.showId].seasons[show.season].episodes[show.episode];
}
let unwatchedShows = [...Object.values(newShowObject)];
for (const show of unwatchedShows) {
show.seasons = [...Object.values(show.seasons)];
for (const season of show.seasons) {
season.episodes = [...Object.values(season.episodes)];
}
show.seasons = show.seasons.filter((season) => season.episodes.length > 0);
}
unwatchedShows = unwatchedShows.filter((show) => show.seasons.length > 0);
The problem is that your benchmark is flawed. Notice that jsbench does run the "setup code" only once per test case, not for each test loop iteration.
Your first solution does mutate the input (particularly season.episodes - while it does clone each show), so only on the first run it actually gives the correct output. On all subsequent runs of the test loop, it basically runs on an empty input, which is much faster.
I fixed this here, and now the second solution is the fastest as expected. (Notice the times do now also include creating the object, though).
I'm not sure how you are getting faster results with the second solution, I would triple check results. The JSBench results are legit, and agrees with Big-O notation.
The first solution loops through shows to create a look up table. Then, loops through a nested loop with quad complexity O(n^2).
The second solution loops through nested loops with cubic complexity O(n^3), because it is nested thrice. So, I would expect this algorithm to chew up more time.
The reason for this is called the sum rule. When two loops sit side-by-side, they don't multiply but add up. This makes the complexity O(n + n^2), which can be further reduced to O(n^2) because the first loop becomes negligible as n approaches infinity.

Better way to fill variables from array (Destructuring assignment)

I have this Code in my Javascript File:
const blfTypeAndId = DDid.split("PrerequisitesSelect");
const blfType = blfTypeAndId[0];
const blfId = blfTypeAndId[1];
The blfType is after this either 'block', 'line' or 'field' and the blfId is _B1_L1_F1 while the numbers can be different (this is just for field, for line it would be _B1_L1 and block just _B1)
In a world where Python and Javascript are combined I would just do this:
const blfType, const blfId = DDid.split("PrerequisitesSelect");
Is there a nice way in Javascript to still do this in one line or is my first written code already the best possible solution?
A good job for the Destructuring Assignment
const blfTypeAndId = DDid.split("PrerequisitesSelect");
const [blfType, blfId] = blfTypeAndId;
or even just
const [blfType, blfId] = DDid.split("PrerequisitesSelect");

How can we take like an input the keyboard in JS?

In my project, I need now to take an input from the keyboard.
The user must be able to enter several words and when he presses CTRL+D you exit the program and the result is displayed.
For example we can enter on the terminal :
bob
alicia
cookie
shirley
david
We have the following code :
#!/usr/bin/env node
let chunk = "";
process.stdin.on("data", data => {
chunk += data.toString();
});
process.stdin.on("end", () => {
chunk.replace(/^\s*[\r\n]/gm,"").split(/\s+/).forEach(function (s) {
process.stdout.write(
s === 'bob'
? 'boy \n'
: s === 'alicia'
? 'girl\n'
: s === 'cookie'
? 'dog \n'
: 'unknown \n');
});
});
And when we press CTRL+D we need to obtain this result :
boy
girl
dog
unknown
unknown
Can you help me please to know, how can I code in order to take the keyboard like an input?
Here is an article that explains the basics. I made an example for you down below, you can probably figure out the rest by yourself.
const readline = require('readline');
readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);
let input = [];
let chunk = '';
process.stdin.on('keypress', (str, key) => {
if (key.ctrl && key.name === 'd') {
//Handle exit code here
process.exit();
}
if (key.name === 'return') {
input.push(chunk.replace('\r', ''));
chunk = '';
process.stdout.write('\n');
}
chunk+=str;
process.stdout.write(str);
});
One way of doing it would to loop input until a certain input is taken. Pseudo code example:
While (x≠q){
Take input
}
EDIT: Another way, is to not use the return key to use for spacing, instead take the items in all in one input line with a separating comma or space.
var str = "123, 124, 234,252";
var arr = str.split(",").map(val => Number(val) + 1);
console.log(arr);
I found the above from this question: How to split and modify a string in NodeJS?.
Then you can iterate over the array to find out if it's a dog or a girl!

Categories

Resources