How to return the array after altering it back into json format? - node.js

I am making a discord bot where you can use the command tcp freeitem to obtain your free item.
I am trying to alter that value of an Account by adding a new item object into the account. When I map the array to replace a value, it erases the name (allAccounts) of the array of the json. More information below. Here is what I have:
const listOfAllItemNames = require(`C:/Users///censored///OneDrive/Desktop/discord bot/itemsDataList.json`)
const accountList = require(`C:/Users///censored///OneDrive/Desktop/discord bot/account.json`)
const fs = require('fs')
var accountThatWantsFreeItem = accountList.allAccounts.find(user => message.author.id === user.userId);
var randomFreeItem = listOfAllItemNames.allItems[Math.floor(Math.random() * listOfAllItemNames.allItems.length)]
if(accountThatWantsFreeItem === undefined) {message.reply('You need to make an account with tcp create!'); return; }
if(accountThatWantsFreeItem.freeItem === true) {message.reply('You already got your free one item!'); return;}
fs.readFile('C:/Users///censored///OneDrive/Desktop/discord bot/account.json', 'utf8', function readFileCallback(err,data) {
if(err){
console.error(err)
} else {
var accountsArray = JSON.parse(data)
console.log(accountsArray)
var whoSentCommand = accountsArray.allAccounts.find(user => message.author.id === user.userId)
whoSentCommand.Items.push(randomFreeItem)
whoSentCommand.freeItem = true;
var test = accountsArray.allAccounts.map(obj => whoSentCommand === obj.id || obj)
//I believe the issue is trying to map it returns a new array
console.log(test)
test = JSON.stringify(test, null, 5)
//fs.writeFile('C:/Users///censored///OneDrive/Desktop/discord bot/account.json', test, err =>{ console.error(err)} )
}
})
when I write the file back to json file, it removes the "allAccounts" identifier in this file
//json file
//array name "allAccounts" is removed, I need this still here for code to work
{
"allAccounts" : [
{
"userId": "182326315813306368",
"username": "serendipity",
"balanceInHand": 0,
"balanceInBank": 0,
"freeItem": false,
"Items": []
},
(No "allAccounts" array name)
to this: output after writing file
So, the final question is
How would I alter the array so that I only alter the account I want without editing the array name?
Please feel free to ask any questions if I was unclear.

Array.map() method returns the converted array.
So in the below line, map() method takes allAccounts array and perform actions and put the target array (not object) to the test variable.
var test = accountsArray.allAccounts.map(obj => whoSentCommand === obj.id || obj)
So for making code works, please change the code like this:
var test = {
"accountsArray": accountsArray.allAccounts.map(obj => whoSentCommand === obj.id || obj)
}

When posting questions, please please reduce the code to a minimal example that will demonstrate the problem, and use words, not code, to describe the problem.
It looks like you are expecting .map to do something other than what it does.
Please consult the documentation for Array.map().
It takes the array that you pass it (in this case accountsArray.allAccounts) and transforms it, returning the transformed array.
You have essentially done test = accountsArray.allAccounts but for some reason are expecting test to contain an Object with the key allAccounts, when in fact it will only contain an Array, because that is what you have assigned it.

Related

Best way to search march in an array of JavaScript Objects?

I'm facing a challenge to match values within an array of JS objects. Let me give you an example,
var dynamicObjectArray = [
{Id: 1422859025, FromTime: "2023-02-12T19:00:00Z", ToTime: "2023-02-12T20:00:00Z"},
{Id: 1422859027, FromTime: "2023-02-12T18:00:00Z", ToTime: "2023-02-12T19:00:00Z"}
]
I need to find all Ids for FromTime or ToTime match with "2023-02-12T19:00:00Z"
Using a database, it can be done easily. But, here, I need to play an array of 100 objects max. Also, data will be dynamic. Can you please suggest me a way to achieve the goal using NodeJS.
You could use the JS Date builtin constructor to compare dates instead of using the string directly.
Filter your matching objects, and map the ids.
You can do something like this.
const dynamicObjectArray = [{
Id: 1422859025,
FromTime: "2023-02-12T19:00:00Z",
ToTime: "2023-02-12T20:00:00Z"
},
{
Id: 1422859027,
FromTime: "2023-02-12T18:00:00Z",
ToTime: "2023-02-12T19:00:00Z"
}
];
const matchTime = new Date("2023-02-12T19:00:00Z").getTime();
const matchIds = dynamicObjectArray.filter(obj => {
const fromTime = new Date(obj.FromTime).getTime();
const toTime = new Date(obj.ToTime).getTime();
return matchTime === fromTime || matchTime === toTime;
}).map(obj => obj.Id);
console.log(matchIds);
If you want a suggestion, you can do something like this:
Create a function that takes as a parameter, the wanted Date
Create a variable containing an array where you will save all those "id's" that match your condition
Create a for loop
Create an if condition, that matches the following condition:
if date exists on "FromTime" or exists on "ToTime", push to your "id's array"
Return your ids array
Here is the code implementation:
function filterObjByDate(TO_MATCH_DATE) {
let ids = [];
for (let i = 0; i < dynamicObjectArray.length; i++) {
if (
dynamicObjectArray[i].FromTime === TO_MATCH_DATE ||
dynamicObjectArray[i].ToTime === TO_MATCH_DATE
) {
ids.push(dynamicObjectArray[i].Id);
}
}
return ids
}
That's it. If you have more question, ask me 😀👍

Best way to organize firebase writes on update trigger

There may be more than one correct answer to this question, but here's my issue: I have a user document in firebase with many fields that can be updated and which interact in different ways. If one field is updated, it may require a change to another field on the backend. Is it better to have a whole bunch of if statements each with their own write action if the condition is met or, or do single write at the end of the function for all the fields that might change. If a field does not change, I would have to write its original value back to itself. That seems clunky, but so does the other option. Am I missing a third option? What is the best practice here?
Here's an example of what I'm talking about. Updating fields one at a time is what I have now, which looks like this:
export const userUpdate = functions.firestore
.document("users/{userID}")
.onUpdate(async (change) => {
const beforeData = change.before.data();
const afterData = change.after.data();
// user levels up and gets more HP
if(beforeData.userLevel != afterData.userLevel){
const newMaxHP = 15 + 5 * afterData.userLevel;
change.after.ref.update({
maxHp: newMaxHP
})
}
//update user rating
if (beforeData.numberOfRatings != afterData.numberOfRatings) {
const newRating = placerRating(beforeData.userRating, beforeData.numberOfRatings, afterData.latestRating);
change.after.ref.update({
userRating: newRating
})
}
//replenish user funds from zero
if (afterData.money == 0){
change.after.ref.update({
money: 20
})
}
If I did it all in a single write, the if statements would assign a value to a variable, but not update the firestore document. Each if statement would include an else statement assigning the variable to the field's original value. There would be a single write at the end like this:
change.after.ref.update({
maxHp: newMaxHP,
userRating: newRating,
money: 20
})
I hope that helps.
[edit to add follow-up question about updating a map value]
#Dharmaraj's answer works great, but I'm struggling to apply it when updating a map value. BTW - I'm using Typescript.
Before using #Dharmaraj's solution, I was doing this:
admin.firestore().collection("users").doc(lastPlayerAttacker).update({
"equipped.weapon.usesLeft": admin.firestore.FieldValue.increment(-1)
});
Using the update object, I'm trying it like this, but I get the error "Object is of type 'unknown'"
const lastPlayerUpdates:{[key:string]:unknown} = {};
lastPlayerUpdates.equipped.weapon.usesLeft = admin.firestore.FieldValue.increment(-1);
admin.firestore().collection("users").doc(lastPlayerAttacker).update(lastPlayerUpdates);
Any advice on how to fix it?
Every time you call update(), you are being charged for 1 write operation. It'll be best to accumulate all updated fields in an object and then update the document only once as it'll be more efficient too. Try refactoring the code as shown below:
export const userUpdate = functions.firestore
.document("users/{userID}")
.onUpdate(async (change) => {
const beforeData = change.before.data();
const afterData = change.after.data();
const updatedData = {};
// user levels up and gets more HP
if (beforeData.userLevel != afterData.userLevel) {
const newMaxHP = 15 + 5 * afterData.userLevel;
updatedData.maxHp = newMaxHP;
}
//update user rating
if (beforeData.numberOfRatings != afterData.numberOfRatings) {
const newRating = placerRating(beforeData.userRating, beforeData.numberOfRatings, afterData.latestRating);
updatedData.userRating = newRating;
}
//replenish user funds from zero
if (afterData.money == 0) {
updatedData.money = 20;
}
await change.after.ref.update(updatedData);
console.log("Data updated");
return null;
})

How to remove same named objects from a JSON structure, but with a non uniform pattern in structure?

I have a json structure, that is a bit odd, this is returned from a remote device, and I have to accept it as is. For example...
{"_":"e6a7f749","4321013c":{"_":"5d839a60"},"67ea44a0":{"_":"ec7500f9"},"6bea5f08":{"_":"ecdaead4"},"1e92311e":{"_":"5348dab3"}}
I need to remove the '_' key-value pairs, but retain everything else, such that...
{"4321013c":,"67ea44a0":,"6bea5f08":,"1e92311e":,}
As you can see, this leaves just some 'keys', from the original structure. I then want to convert the 'keys' to a simple array of values. Such...
["4321013c","67ea44a0","6bea5f08","1e92311e"]
All this done in node.js (JavaScript) by the way.
Here is my solution. I am not sure if I fully understood the question though.
But here I traverse the object, an gather up all the keys and values into another, flattened object. Then finally, I run Object.keys on that and remove any "_" values.
const obj = {
"_":"e6a7f749",
"4321013c":{"_":"5d839a60"},
"67ea44a0":{"_":"ec7500f9"},
"6bea5f08":{"_":"ecdaead4"},
"1e92311e":{"_":"5348dab3"}
};
const collectValues = (root) => {
return Object.keys(root).reduce((map, key) => {
const value = root[key];
if (typeof value === 'string') {
map = {
...map,
[key]: true,
[value]: true
};
} else {
const nestedValues = collectValues(root[key]);
map = {
...map,
...nestedValues,
[key]: true
};
}
return map;
}, {});
}
const uniqueArray = Object.keys(collectValues(obj)).filter(v => v !== '_');
console.log(uniqueArray);

How do I filter keys from JSON in Node.js?

I'm trying to select certain keys from an JSON array, and filter the rest.
var json = JSON.stringify(body);
which is:
{
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
},
"more keys": "foo",
"unwanted key": "foo"
}
Want I want:
{
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
}
}
I've checked out How to filter JSON data in node.js?, but I'm looking to do this without any packages.
Now you can use Object.fromEntries like so:
Object.fromEntries(Object.entries(raw).filter(([key]) => wantedKeys.includes(key)))
You need to filter your obj before passing it to json stringify:
const rawJson = {
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
},
"more keys": "foo",
"unwanted key": "foo"
};
// This array will serve as a whitelist to select keys you want to keep in rawJson
const filterArray = [
"FirstName",
"typeform_form_submits",
];
// this function filters source keys (one level deep) according to whitelist
function filterObj(source, whiteList) {
const res = {};
// iterate over each keys of source
Object.keys(source).forEach((key) => {
// if whiteList contains the current key, add this key to res
if (whiteList.indexOf(key) !== -1) {
res[key] = source[key];
}
});
return res;
}
// outputs the desired result
console.log(JSON.stringify(filterObj(rawJson, filterArray)));
var raw = {
"FirstName":"foo",
"typeform_form_submits":{
"foo":true,
"bar":true,
"baz":true
},
"more keys": "foo",
"unwanted key": "foo"
}
var wantedKeys =["FirstName","typeform_form_submits" ]
var opObj = {}
Object.keys(raw).forEach( key => {
if(wantedKeys.includes(key)){
opObj[key] = raw[key]
}
})
console.log(JSON.stringify(opObj))
I know this question was asked aways back, but I wanted to just toss out there, since nobody else did:
If you're bound and determined to do this with stringify, one of its less-well-known capabilities involves replacer, it's second parameter. For example:
// Creating a demo data set
let dataToReduce = {a:1, b:2, c:3, d:4, e:5};
console.log('Demo data:', dataToReduce);
// Providing an array to reduce the results down to only those specified.
let reducedData = JSON.stringify(dataToReduce, ['a','c','e']);
console.log('Using [reducer] as an array of IDs:', reducedData);
// Running a function against the key/value pairs to reduce the results down to those desired.
let processedData = JSON.stringify(dataToReduce, (key, value) => (value%2 === 0) ? undefined: value);
console.log('Using [reducer] as an operation on the values:', processedData);
// And, of course, restoring them back to their original object format:
console.log('Restoration of the results:', '\nreducedData:', JSON.parse(reducedData), '\nprocessedData:', JSON.parse(processedData));
In the above code snippet, the key value pairs are filtered using stringify exclusively:
In the first case, by providing an array of strings, representing the keys you wish to preserve (as you were requesting)
In the second, by running a function against the values, and dynamically determining those to keep (which you didn't request, but is part of the same property, and may help someone else)
In the third, their respective conversions back to JSON (using .parse()).
Now, I want to stress that I'm not advocating this as the appropriate method to reduce an object (though it will make a clean SHALLOW copy of said object, and is actually surprisingly performant), if only from an obscurity/readability standpoint, but it IS a totally-effective (and mainstream; that is: it's built into the language, not a hack) option/tool to add to the arsenal.

How do I get the result of class getters into JSON? [duplicate]

Take this object:
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
If I do this:
y = JSON.parse( JSON.stringify(x) );
Then y will return { "key1": "xxx" }. Is there anything one could do to transfer functions via stringify? Creating an object with attached functions is possible with the "ye goode olde eval()", but whats with packing it?
json-stringify-function is a similar post to this one.
A snippet discovered via that post may be useful to anyone stumbling across this answer. It works by making use of the replacer parameter in JSON.stringify and the reviver parameter in JSON.parse.
More specifically, when a value happens to be of type function, .toString() is called on it via the replacer. When it comes time to parse, eval() is performed via the reviver when a function is present in string form.
var JSONfn;
if (!JSONfn) {
JSONfn = {};
}
(function () {
JSONfn.stringify = function(obj) {
return JSON.stringify(obj,function(key, value){
return (typeof value === 'function' ) ? value.toString() : value;
});
}
JSONfn.parse = function(str) {
return JSON.parse(str,function(key, value){
if(typeof value != 'string') return value;
return ( value.substring(0,8) == 'function') ? eval('('+value+')') : value;
});
}
}());
Code Snippet taken from Vadim Kiryukhin's JSONfn.js or see documentation at Home Page
I've had a similar requirement lately. To be clear, the output looks like JSON but in fact is just javascript.
JSON.stringify works well in most cases, but "fails" with functions.
I got it working with a few tricks:
make use of replacer (2nd parameter of JSON.stringify())
use func.toString() to get the JS code for a function
remember which functions have been stringified and replace them directly in the result
And here's how it looks like:
// our source data
const source = {
"aaa": 123,
"bbb": function (c) {
// do something
return c + 1;
}
};
// keep a list of serialized functions
const functions = [];
// json replacer - returns a placeholder for functions
const jsonReplacer = function (key, val) {
if (typeof val === 'function') {
functions.push(val.toString());
return "{func_" + (functions.length - 1) + "}";
}
return val;
};
// regex replacer - replaces placeholders with functions
const funcReplacer = function (match, id) {
return functions[id];
};
const result = JSON
.stringify(source, jsonReplacer) // generate json with placeholders
.replace(/"\{func_(\d+)\}"/g, funcReplacer); // replace placeholders with functions
// show the result
document.body.innerText = result;
body { white-space: pre-wrap; font-family: monospace; }
Important: Be careful about the placeholder format - make sure it's not too generic. If you change it, also change the regex as applicable.
Technically this is not JSON, I can also hardly imagine why would you want to do this, but try the following hack:
x.key2 = x.key2.toString();
JSON.stringify(x) //"{"key1":"xxx","key2":"function (){return this.key1}"}"
Of course the first line can be automated by iterating recursively over the object. Reverse operation is harder - function is only a string, eval will work, but you have to guess whether a given key contains a stringified function code or not.
You can't pack functions since the data they close over is not visible to any serializer.
Even Mozilla's uneval cannot pack closures properly.
Your best bet, is to use a reviver and a replacer.
https://yuilibrary.com/yui/docs/json/json-freeze-thaw.html
The reviver function passed to JSON.parse is applied to all key:value pairs in the raw parsed object from the deepest keys to the highest level. In our case, this means that the name and discovered properties will be passed through the reviver, and then the object containing those keys will be passed through.
This is what I did https://gist.github.com/Lepozepo/3275d686bc56e4fb5d11d27ef330a8ed
function stringifyWithFunctions(object) {
return JSON.stringify(object, (key, val) => {
if (typeof val === 'function') {
return `(${val})`; // make it a string, surround it by parenthesis to ensure we can revive it as an anonymous function
}
return val;
});
};
function parseWithFunctions(obj) {
return JSON.parse(obj, (k, v) => {
if (typeof v === 'string' && v.indexOf('function') >= 0) {
return eval(v);
}
return v;
});
};
The naughty but effective way would be to simply:
Function.prototype.toJSON = function() { return this.toString(); }
Though your real problem (aside from modifying the prototype of Function) would be deserialization without the use of eval.
I have come up with this solution which will take care of conversion of functions (no eval). All you have to do is put this code before you use JSON methods. Usage is exactly the same but right now it takes only one param value to convert to a JSON string, so if you pass remaning replacer and space params, they will be ignored.
void function () {
window.JSON = Object.create(JSON)
JSON.stringify = function (obj) {
return JSON.__proto__.stringify(obj, function (key, value) {
if (typeof value === 'function') {
return value.toString()
}
return value
})
}
JSON.parse = function (obj) {
return JSON.__proto__.parse(obj, function (key, value) {
if (typeof value === 'string' && value.slice(0, 8) == 'function') {
return Function('return ' + value)()
}
return value
})
}
}()
// YOUR CODE GOES BELOW HERE
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
const y = JSON.parse(JSON.stringify(x))
console.log(y.key2())
It is entirely possible to create functions from string without eval()
var obj = {a:function(a,b){
return a+b;
}};
var serialized = JSON.stringify(obj, function(k,v){
//special treatment for function types
if(typeof v === "function")
return v.toString();//we save the function as string
return v;
});
/*output:
"{"a":"function (a,b){\n return a+b;\n }"}"
*/
now some magic to turn string into function with this function
var compileFunction = function(str){
//find parameters
var pstart = str.indexOf('('), pend = str.indexOf(')');
var params = str.substring(pstart+1, pend);
params = params.trim();
//find function body
var bstart = str.indexOf('{'), bend = str.lastIndexOf('}');
var str = str.substring(bstart+1, bend);
return Function(params, str);
}
now use JSON.parse with reviver
var revivedObj = JSON.parse(serialized, function(k,v){
// there is probably a better way to determ if a value is a function string
if(typeof v === "string" && v.indexOf("function") !== -1)
return compileFunction(v);
return v;
});
//output:
revivedObj.a
function anonymous(a,b
/**/) {
return a+b;
}
revivedObj.a(1,2)
3
To my knowledge, there are no serialization libraries that persist functions - in any language. Serialization is what one does to preserve data. Compilation is what one does to preserve functions.
It seems that people landing here are dealing with structures that would be valid JSON if not for the fact that they contain functions. So how do we handle stringifying these structures?
I ran into the problem while writing a script to modify RequireJS configurations. This is how I did it. First, there's a bit of code earlier that makes sure that the placeholder used internally (">>>F<<<") does not show up as a value in the RequireJS configuration. Very unlikely to happen but better safe than sorry. The input configuration is read as a JavaScript Object, which may contain arrays, atomic values, other Objects and functions. It would be straightforwardly stringifiable as JSON if functions were not present. This configuration is the config object in the code that follows:
// Holds functions we encounter.
var functions = [];
var placeholder = ">>>F<<<";
// This handler just records a function object in `functions` and returns the
// placeholder as the value to insert into the JSON structure.
function handler(key, value) {
if (value instanceof Function) {
functions.push(value);
return placeholder;
}
return value;
}
// We stringify, using our custom handler.
var pre = JSON.stringify(config, handler, 4);
// Then we replace the placeholders in order they were encountered, with
// the functions we've recorded.
var post = pre.replace(new RegExp('"' + placeholder + '"', 'g'),
functions.shift.bind(functions));
The post variable contains the final value. This code relies on the fact that the order in which handler is called is the same as the order of the various pieces of data in the final JSON. I've checked the ECMAScript 5th edition, which defines the stringification algorithm and cannot find a case where there would be an ordering problem. If this algorithm were to change in a future edition the fix would be to use unique placholders for function and use these to refer back to the functions which would be stored in an associative array mapping unique placeholders to functions.

Resources