Node.js; list strings from JSON file in order - node.js

I have file users.json, which contain, among others, data like this:
[
{
"nick": "user123",
"active24": 579
...
}, {
"nick": "nick",
"active24": 259
...
}
]
I want to make leaderboard, showing top 10 users nicks (from uknown max value of users), based on ther "active24" (from biggest to smallest). I can do it using array, but problem is that bot can't send it using vk-io (reason is not important, so i don't describe it much here, but please, do not save final input as array, but as variables or etc). I want output like:
Var_top1 = `${nick}: ${active24}`;
Var_top2 = `${nick}: ${active24}`;
Var_top3 = `${nick}: ${active24}`;
...
Var_top10 = `${nick}: ${active24}`;
P.S sorry for my bad English

do you want something like this?
const data = [
{
nick: "user111",
active24: 579
},
{
nick: "nick222",
active24: 0
},
{
nick: "nick333",
active24: 84
},
{
nick: "nick444",
active24: 9459
}
];
const sorted = data.sort((a, b) => b.active24 - a.active24);
console.log(JSON.stringify(sorted));
//[{"nick":"nick444","active24":9459},{"nick":"user111","active24":579},{"nick":"nick333","active24":84},{"nick":"nick222","active24":0}]
const top1 = sorted[0];
const top2 = sorted[1];
const top3 = sorted[2];
const top4 = sorted[3];

Order the array using a methodology such as
array.prototype.sort.
Once the array has all members sorted, you just need to print them array[0].nick + ":" + array[0].active24
let jsonObject = [
{
"nick": "user123",
"active24": 579
},
{
"nick": "user234",
"active24": 1
},
{
"nick": "nick",
"active24": 259
}
];
// Orders the array
jsonObject.sort((firstElem, secondElem) => {
if (firstElem.active24 > secondElem.active24) return 1;
if (firstElem.active24 < secondElem.active24) return -1;
return 0;
})
// Prints the entire ordered array
console.log(JSON.stringify(jsonObject, null, 2));
// Prints the nick and the active24 value
for (let i = 0; i < jsonObject.length; i++) {
console.log(jsonObject[i].nick + ":" + jsonObject[i].active24);
}

Related

Is there a way to map a json object for the below example?

excel snippet
I am using Mule 4 and am trying to read an excel file, then convert into JSON using Dataweave and update them in salesforce.
Below is the payload which I am getting while I read the excel. I need to convert this into the requested Output Payload.
The values are dynamic. There might be more objects.
Any ideas appreciated.
Thanks.
Input Payload:
{
"X":[
{
"A":"Key1",
"B":"Key2",
"C":"Key3",
"D":"value1",
"E":"value2"
},
{
"A":"",
"B":"",
"C":"Key4",
"D":"value3",
"E":"value4"
},
{
"A":"Key5",
"B":"Key6",
"C":"Key7",
"D":"Value5",
"E":"Value6"
},
{
"A":"",
"B":"",
"C":"Key8",
"D":"Value7",
"E":"Value8"
}
]
}
Output Payload:
[
{
"Key1":{
"Key2":{
"Key3":"value1",
"Key4":"value3"
}
},
"Key5":{
"Key6":{
"Key7":"Value5",
"Key8":"Value7"
}
}
},
{
"Key1":{
"Key2":{
"Key3":"value2",
"Key4":"value4"
}
},
"Key5":{
"Key6":{
"Key7":"Value6",
"Key8":"Value8"
}
}
}
]
The following seems to work.
This is JavaScript. I don't know what the underlying syntax or scripting language is for DataWeave, but between the C-family syntax and inline comments you can probably treat the JS like pseudo-code and read it well enough to recreate the logic.
// I assume you started with a table structure like this:
//
// A B C D E
// == == == == ==
// K1 K2 K3 v1 v2 <~ X[0]
// __ __ K4 v3 v4 <~ X[1]
// K5 K6 K7 v5 v6 <~ X[2]
// __ __ K8 v7 v8 <~ X[3]
//
// So I'm going to call A,B,C,D,E "column labels"
// and the elements in `X` "rows".
// Here's the original input you provided:
input = {
"X": [
{
"A":"Key1",
"B":"Key2",
"C":"Key3",
"D":"value1",
"E":"value2"
},
{
"A":"",
"B":"",
"C":"Key4",
"D":"value3",
"E":"value4"
},
{
"A":"Key5",
"B":"Key6",
"C":"Key7",
"D":"Value5",
"E":"Value6"
},
{
"A":"",
"B":"",
"C":"Key8",
"D":"Value7",
"E":"Value8"
}
]
}
// First let's simplify the structure by filling in the missing keys at
// `X[1].A`, `X[1].B` etc. We could keep track of the last non-blank
// value while doing the processing below instead, but doing it now
// reduces the complexity of the final loop.
input.X.forEach((row, row_index) => {
(Object.keys(row)).forEach((col_label) => {
if (row[col_label].length == 0) {
row[col_label] = input.X[row_index - 1][col_label]
}
});
});
// Now X[1].A is "Key1", X[1].B is "Key2", etc.
// I'm not quite sure if there's a hard-and-fast rule that determines
// which values become keys and which become values, so I'm just going
// to explicitly describe the structure. If there's a pattern to follow
// you could compute this dynamically.
const key_column_labels = ["A","B","C"]
const val_column_labels = ["D","E"]
// this will be the root object we're building
var output_list = []
// since the value columns become output rows we need to invert the loop a bit,
// so the outermost thing we iterate over is the list of value column labels.
// our general strategy is to walk down the "tree" of key-columns and
// append the current value-column. we do that for each input row, and then
// repeat that whole cycle for each value column.
val_column_labels.forEach((vl) => {
// the current output row we're populating
var out_row = {}
output_list.push(out_row)
// for each input row
input.X.forEach((in_row) => {
// start at the root level of the output row
var cur_node = out_row
// for each of our key column labels
key_column_labels.forEach((kl, ki) => {
if (ki == (key_column_labels.length - 1)) {
// this is the last key column (C), the one that holds the values
// so set the current vl as one of the keys
cur_node[in_row[kl]] = in_row[vl]
} else if (cur_node[in_row[kl]] == null) {
// else if there's no map stored in the current node for this
// key value, let's create one
cur_node[in_row[kl]] = {}
// and "step down" into it for the next iteration of the loop
cur_node = cur_node[in_row[kl]]
} else {
// else if there's no map stored in the current node for this
// key value, so let's step down into the existing map
cur_node = cur_node[in_row[kl]]
}
});
});
});
console.log( JSON.stringify(output_list,null,2) )
// When I run this I get the data structure you're looking for:
//
// ```
// $ node json-transform.js
// [
// {
// "Key1": {
// "Key2": {
// "Key3": "value1",
// "Key4": "value3"
// }
// },
// "Key5": {
// "Key6": {
// "Key7": "Value5",
// "Key8": "Value7"
// }
// }
// },
// {
// "Key1": {
// "Key2": {
// "Key3": "value2",
// "Key4": "value4"
// }
// },
// "Key5": {
// "Key6": {
// "Key7": "Value6",
// "Key8": "Value8"
// }
// }
// }
// ]
// ```
Here's a JSFiddle that demonstrates this: https://jsfiddle.net/wcvmu0g9/
I'm not sure this captures the general form you're going for (because I'm not sure I fully understand that), but I think you should be able to abstract this basic principle.
It was a challenging one. I was able to at least get the output you expect with this Dataweave code. I'll put some comments on the code.
%dw 2.0
output application/json
fun getNonEmpty(key, previousKey) =
if(isEmpty(key)) previousKey else key
fun completeKey(item, previousItem) =
{
A: getNonEmpty(item.A,previousItem.A),
B: getNonEmpty(item.B,previousItem.B)
} ++ (item - "A" - "B")
// Here I'm filling up the A and B columns to have the complete path using the previous items ones if they come empty
var completedStructure =
payload.X reduce ((item, acc = []) ->
acc + completeKey(item, acc[-1])
)
// This takes a list, groups it by a field and let you pass also what to
// want to do with the grouped values.
fun groupByKey(structure, field, next) =
structure groupBy ((item, i) -> item[field]) mapObject ((v, k, i1) ->
{
(k): next(k,v)
}
)
// This one was just to not repete the code for each value field
fun valuesForfield(structure, field) =
groupByKey(structure, "A", (key,value) ->
groupByKey(value, "B", (k,v) ->
groupByKey(value, "C", (k,v) -> v[0][field]))
)
var valueColumns = ["D","E"]
---
valueColumns map (value, index) -> valuesForfield(completedStructure,value)
EDIT: valueColumns is now Dynamic

What is causing a double comma in my csv output when using `",\n`?

I am writing a script that is designed to take in an array and replace a designated row in a csv(buffer) then output a csv(buffer) in nodejs. However, I have found that whenever I have the following combination of characters: ",\n", it is doubling the comma. I have tried using \r instead or \n, but the system I am importing the csv has issues with the \r. I also found that by adding an extra whitespace: ", \n it prevents the double comma, but again, the system I'm importing the final result into won't accept the extra space. Does anyone know what is causing the extra comma and/or a way to not get the extra comma?
Script that replaces CSV row:
node.on('input', function(msg) {
node.rowNumber = msg.rowNumber || config.rowNumber || 0; //Row being replaced.
node.newRow = msg.newRow || config.newRow; //New Row Array or Comma Separated String
var payload = msg.file || config.file || RED.util.getMessageProperty(msg, "payload"); //File path or buffer.
if (!Buffer.isBuffer(payload)) {
payload = payload.replace('\\', '/');
payload = fs.readFileSync(payload);
}
if (!Array.isArray(this.newRow)) {
node.newRow = node.newRow.split(',');
}
var dataArray = [];
var csvArr = [];
const readable = new Stream.Readable()
readable._read = () => {}
readable.push(payload)
readable.push(null)
readable.pipe(csv())
.on('data', function (data) {
dataArray.push(data);
})
.on('end', function(){
csvArr.push(Object.keys(dataArray[0]));
dataArray.forEach((item, i) => {
csvArr.push(_.values(item));
});
if (node.rowNumber == 0) {
csvArr.push(node.newRow);
}
else {
csvArr.splice(node.rowNumber - 1, 1, node.newRow);
}
var finalCSV = csvArr.join('\n');
msg.payload = Buffer.from(finalCSV);
node.send(msg); //Returns the msg object
});
});
Input:
[
`""{
""""actions"""":{
""""validation"""":[
],
""""reconciliation"""":[
]
},
""""enforce_all_required_fields"""":"""""""",
""""form_history"""":""""12c2acda35980131f98acf2a39c1aafe"""",
""""form_id"""":""""228"""",
""""options"""":[
],
""""record_action"""":""""both"""",
""""secondary_form_history"""":"""""""",
""""secondary_form_id"""":""""0"""",
""""secondary_form_name"""":"""""""",
""""secondary_is_tier1_form"""":"""""""",
""""selected_columns"""":[
""""field_9326"""",
""""field_3742_first"""",
""""field_3742_last"""",
""""field_9325"""",
""""field_9327"""",
],
""""skip_headers"""":"""""""",
""""target_match_type"""":""""""""
}""`
]
Undesired output:
"{
""actions"":{
""validation"":[
],
""reconciliation"":[
]
},
""enforce_all_required_fields"":"""",,
""form_history"":""12c2acda35980131f98acf2a39c1aafe"",,
""form_id"":""228"",,
""options"":[
],
""record_action"":""both"",,
""secondary_form_history"":"""",,
""secondary_form_id"":""0"",,
""secondary_form_name"":"""",,
""secondary_is_tier1_form"":"""",,
""selected_columns"":[
""field_9326"",,
""field_3742_first"",,
""field_3742_last"",,
""field_9325"",,
""field_9327"",,
],
""skip_headers"":"""",,
""target_match_type"":""""
}"
Notice the double commas?

Unable to run includes() method on string returned by fs.readFileSync()

having a bit of trouble:
let data = fs.readFileSync(pathToCsv, "utf8");
the value of data comes out to be:
clan,mem1,mem2,mem3,mem4,language,managerID,serverJoinedDate
pm,
pm
(through console.log())
but still data.toString().includes("pm") is false.
Here is my full code:
const filter = (m) => m.author.bot === false;
await ogMessage.author.dmChannel
.awaitMessages(filter, {
max: 1,
time: 60000,
})
.then((collected) => {
if (clans[parseInt(collected.toJSON()[0].content) - 1]) {
let clan = clans[parseInt(collected.toJSON()[0].content) - 1];
let data = fs.readFileSync(pathToCsv, "utf8");
console.log(typeof clan);
// let reg = new RegExp(clan, "g");
// let count = (data.match(reg) || []).length;
if (data.split(",").includes(clan)) {
ogMessage.author.send(
"People from this clan are already registered!\nPlease contact the hosts for help!"
);
return;
} else {
teamCheck = true;
}
} else {
ogMessage.author.send("Invalid Clan! Please try again!");
return;
}
})
.catch((collected) => {
try {
console.log("Error" + collected);
} catch (e) {
console.log(e);
}
});
if (teamCheck === false) {
return;
}
I have tried splitting the data, using regular expressions but nothing seems to work on the string returned by
readFileSync()
PS. I am making a discord bot.
In the source string you have pm with a space before it. That's why after calling split("," you end up with the element " pm" in the result array and "pm" is not equal to it.
You just need to trim spaces in all elements before searching some string in it
The problem was in the clans array.
I was defining it as such:
var clans = fs.readFileSync(pathToData, "utf8").split("\n");
but the problem was that the readFileSync() method added an "\r" after every string of the array. That is why it was not able to match the clan string to the data
So what worked was var clans = fs.readFileSync(pathToData, "utf8").split("\r\n");
Now, the array includes only the string, and the includes() method can find a match!

How do i get a randomly selected string name and still select an existing substring without the code coming out as text

I'm currently trying to get info off of an object but that's randomly selected. I believe that the true problem is that what I wrote is not being taken as a variable for selecting an existing object if not as the variable for the object, I don't know if this is a clear message or not.
Example of what I have tried:
let PK = ["Random1", "Random2", "Random3"]
let PKS = Math.floor(Math.random() * PK.length)
let Random1 = {
name: "Random1",
number: "010"
}
let Random2 = {
name: "Random2",
number: "011"
}
if(message.content.toLowerCase() == "random"){
message.channel.send(PK[PKS].number)
}
There is another thing I have tried which is by using ${}. These are the results:
"Random1.number" or "Random2.number" when what I was looking for is actually "010" or "011".
You should wrap your objects inside some collection such as an Array and then you just compare the value from your random selection to the value find in the collection (if any (randoms)):
let PK = ["Random1", "Random2", "Random3"];
let PKS = Math.floor(Math.random() * PK.length);
const randoms = [
{
name: "Random1",
number: "010",
},
{
name: "Random2",
number: "011",
},
];
if (message.content.toLowerCase() == "random") {
const findRandom = randoms.find((v) => v.name === PK[PKS]);
if (findRandom) {
message.channel.send(findRandom.number);
}
}

Leaderboards discord js

I'm storing the xp in a json file for practice. How can I display the top 10 users? The first time I
would have wanted to sort but I don't know how it would be more efficient.
let xpAdd = Math.floor(Math.random() * 9) + 8;
if(!xp[message.author.id]) {
xp[message.author.id] = {
xp:0,
level:1
};
}
let curxp = xp[message.author.id].xp;
let curlvl = xp[message.author.id].level;
let nxtLevel = xp[message.author.id].level * 300;
xp[message.author.id].xp = curxp + xpAdd;
fs.writeFile("./xp.json", JSON.stringify(xp), (error) => {
if(error) console.log(error);
});
This is the code I store
And with this I show the level of xp
if(!xp[message.author.id]) {
xp[message.author.id] = {
xp: 0,
level:1
};
}
let curxp = xp[message.author.id].xp;
let curlvl = xp[message.author.id].level;
let nxtLevelXp = curlvl * 300;
let difference = nxtLevelXp - curxp;
I'd suggest you should convert your object to an array so that you can format it and sort it however you prefer.
// sample object
// I'm going to show the top three in this example for the interest of space
const xp = {
"ID #1": {
level: 3,
xp: 300,
},
"ID #2": {
level: 4,
xp: 400,
},
"ID #3": {
level: 2,
xp: 200,
},
"ID #4": {
level: 1,
xp: 100,
},
};
// sort entries in order of exp (descending), then single out the top three
let entries = Object.entries(xp).sort(([, a], [, b]) => b.xp > a.xp ? 1 : -1).slice(0, 3);
// map entries to the prefered format using the data in their objects
// (in your actual command, you should use `client.users.cache.get(id).tag`,
// but since I don't have access to the client object here, I'm just writing `id`
entries = entries.map(([id, { level, xp }], idx) => `${idx + 1} - ${id} (level ${level}; ${xp} xp)`);
// join each user by a line break
console.log(entries.join('\n'));
Working With Objects
Object.entries()
Array.prototype.sort()
Array.prototype.map()
Array.prototype.join()

Resources