I have a javascript file that has an object I'd like to be read by Python (Python 3 is just fine). Something like this:
let variable_i_do_not_want = 'foo'
let function_i_do_not_wnt = function() {
}
// .. etc ..
// --- begin object I want ---
myObject = {
var1: 'value-1',
var2: 'value-2',
fn2: function() {
"I don't need functions.."
},
mySubObject: {
var3: 'value-3',
.. etc ..
}
}
// --- end object I want ---
// .. more stuff I don't want ..
I want to convert myObject to a python dict object. Note I don't really need the functions, just keys and values.
I'm fine with (and capable) adding comment markers before/after and isolating the object. But I think I need a library to convert that string into a Python dict. Is this possible?
Doing this using python would be a lot of work that can be avoided if you could add a few things directly in your javascript file. (as you told you could modify the js file)
I assume that you have nodejs and npm preinstalled (if not then you can install it from here
You need to add these lines of code at the end of the JS file.
const fs = require("fs");
const getVals = (obj) => {
let myData = {};
for (const key in obj) {
if (
!(typeof obj[key] === "function") && // ignore functions
(!(typeof obj[key] == "object") || Array.isArray(obj[key])) // ignore objects (except arrays)
) {
myData[key] = obj[key];
} else if (typeof obj[key] === "object") { // if it's an object, recurse
myData = {
...myData,
...getVals(obj[key]),
};
}
}
return myData;
};
// storing the data into a json file
fs.writeFile(
"myjsonfile.json",
JSON.stringify(JSON.stringify(getVals(myObject))), //change your variable name here instead of myObject (if needed)
(err) => {
if (err) throw err;
console.log("complete");
}
);
once you add this you can run the js file by
~$ npm init -y
~$ node yourjsfile.js
This will make a new file named myjsonfile.json with the data which you can load from python like this
import json
with open('myjsonfile.json') as file:
d=json.loads(file.read()) #your dict
print(d)
;)
Related
I am using node.js, and i want to add the arguments from a command and the server id as a key:value pair into a json file like this:
{
"guildid": "args",
"guildid2": "args2",
}
And the current code that i have is far from what i want, where this code:
const command = args.shift().toLowerCase();
const args = message.content.slice(config.prefix.length).trim().split(' ');
if (command === 'setup') {
const guildid = message.guild.id
const data = {
[guildid]: `${args}`
}
const groupname = JSON.stringify(data, null, 2);
fs.writeFile('./groups.json', groupname,{flags: "a"}, finished);
function finished(err) {
message.channel.send(`Success! Your group, **${args}**, has been registered to **${message.guild.name}**`
)}
Outputs what i want to the json file, but if i run the command again it just appends to the end:
{
"guildid": "args"
}{
"guildid2": "args2"
}
I understand now that using the a flag just appends to the end no matter what and const data is what is giving it the brackets, but i want to know how to be able to format it in the way i showed at the beginning. Apologies for any glaring errors i have made, this is one of my first times using javascript and node.js.
You'll have to get the original file first using fs.readFile[Sync] and JSON.parse, and then add on the property as you would any other object:
obj.key = 'value';
// or in your case:
obj[key_variable] = 'value';
You should also omit the 'a' flag as it will append another object (resulting in a syntax error) instead of modifying the one already there. Your code should look like this:
const guildid = message.guild.id;
const data = JSON.parse(fs.readFileSync('/groups.json'));
/*
You could either use:
data[guildid] = `${args}`;
OR, when stringifying to the file:
const groupname = JSON.stringify({
...data,
[guildid]: `${args}`
}, null, 2);
Either way will work
*/
data[guildid] = `${args}`;
const groupname = JSON.stringify(data, null, 2);
// remove { flags: 'a' }
fs.writeFile('./groups.json', groupname, finished);
// you should probably write some error handling
function finished(err) {
message.channel.send(
`Success! Your group, **${args}**, has been registered to **${message.guild.name}**`
);
}
I've a image input in my webpage and input's output (File object) is saved inside the Question class. questionArr is a array of Question objects
let questionsArr = []; // Array of Question
class Question {
constructor(id) {
this.id = id;
this.image = false;
}
}
when the input value of image input changes, following function calls.
const handleImages = evt => {
let id = evt.target.id; // quizCoverImg or a integer (0,1,...)
const file = evt.target.files[0];
if (file && file.type.startsWith("image/")) {
if (id == "quizCoverImg") {
coverImage = file; // declared in top of the code
// console.log(coverImage) => File {name: "cat.png", lastModified ...}
// Returns a file object, which is correct
} else {
questionsArr[id].image = file;
// console.log(questionsArr[id].image) => File {name: "cat.png", lastModified ...}
// Returns a file object, which is correct
}
}
};
To this point everything works fine. Problem arise when I use above variables somewhere eles
const somewhereElse = () => {
console.log(coverImage); // File {name: "cat.png", lastModified ...} ✔
console.log(typeof coverImage); // object ✔
console.log(questionsArr[0].image); // C:\fakepath\cat.jpg ❓ should return a file object as mentioned above
console.log(typeof questionsArr[0].image); // string ❓
}
I know FileReader() exist, but I want to figure out why I'm getting two different outputs here.
Issue occurred in svelte#3.22.2
Edit 1: Places where questionArr used
This add Question to array
const addQuestion = () => {
const q = new Question(n);
questionsArr = [...questionsArr, q]; // because I'm using svelte :)
n++;
};
Then used in above handleImage()
The key difference is in the toString() method that affects what you are looking at. There is not much context to help debug the details of exactly how you are running this and how you are inspecting the values.
When you have selected a file in a form file input, the browser converts the path to a "fakepath" so that while the script can access the selected file, it cannot learn about the user's directory structure. The filename/path is a reasonable default toString result when trying to inspect/print the file object.
I've decided on splitting my environments keeping them in .js files in an environment folder and keep all the sensitive information in .env file (use a third-party module 'DOTENV')
That's what I've come up with but I understand that it's not the best practice and there are a lot of things which should have been implemented in a completely different way but I just lack experience and practice.
At first, I tried to use as more " for loop " as it's possible because as far as I know, it's the fastest way to loop through an object, but in some cases, it was much easier to with "map or filter".
It doesn't look nice to assign data by returning a Promise. Maybe there is a way to get data without a Promise?
I would appreciate any suggestions on how the code can be improved and good practices, your experience.
And I am not sure if I used logging right and error handling. That's a completely new thing for me at the moment, but I used "try catch" to catch them and simply logged them on the console and put into a file.
code:
import { readdirSync } from 'fs';
import path from "path";
import { logger } from '../src/utils/logging';
import { merge } from "lodash";
// FIXME: Function returns a Promise with the data.
// It's not comfortable and seem a bad practice - too much code for a simple task,
// and deal with a promise what may outcome in decreasing perfomance
// ( The simplest code, the fastest code )
export let env = getEnvironment().then(
res => { return res },
err => logger.error(err)
);
// TODO: Rewrite this function into a class Environment to keep it organized and implement ES6 standart
async function getEnvironment() {
const mode = process.env.NODE_ENV || 'development';
const rootPath = process.cwd();
const folder = 'environment';
const loadEnvironments = () => {
// Getting the list of available environments in the "environment" folder,
// at the same time excluding index.js file
const list = readdirSync(path.join(rootPath, folder)).filter(file => !/(?=^(index.js))/i.test(file));
const parameters = {};
// Loading the files found in the folder,
// merging them with the help of a "lodash" library
// just to get one common Object with all possible parameters from all found environments
const loaded = list.map(fileName => {
let name = fileName.split('.')[0];
let loadedFile = require(path.join(rootPath, folder, fileName));
const file = loadedFile[name];
merge(parameters, { ...file });
return loadedFile;
});
// Picking the currect mode out of already loaded ones
const current = { ...loaded.filter(file => file[mode]).map(file => file[mode])[0] };
// Returning an object with all parameters
return {
parameters,
current
}
};
const environments = loadEnvironments();
const environment = {} = looping(environments.parameters, environments.current);
function looping(obj, values) {
const collection = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] !== 'object') {
try {
if (values.hasOwnProperty(key)) {
// By a recursive function run through all parameters,
// transforming the keys to uppercased,
// assigning value to 'obj' (file containing all the parameters)
// from the current mode
collection[key.toUpperCase()] = values[key];
} else {
// if there is no such a key in the current mode,
// 'null' is assigned
collection[key.toUpperCase()] = null;
}
} catch (e) {
logger.error(` Missing parameter "${key.toUpperCase()}" in ${mode} mode!!!`);
}
} else {
// Recursing through the object and the nested objects
collection[key.toUpperCase()] = looping(obj[key], values[key]);
}
}
}
return collection;
}
// When parameters are ready,
// the current mode is assigned
environment["MODE"] = mode;
return environment;
}
I'm currently searching for a way to create a JSON file (versions.json) with a key and a value from an object within JavaScript. To create the JSON file, I've this object here:
["V1_config-interfaces.json","V2_config-interfaces.json","V3_config-interfaces.json","versions.json"]
I need to loop now some way over this object and check if the current file is not the versions.json because this is the created file.
The JSON file must looks like this:
{
"V1": "V1_config-interfaces.json",
"V2": "V2_config-interfaces.json",
"V3": "V3_config-interfaces.json"
}
So the key is always the version number before the underscore. What I've tried is this here:
const fs = require('fs');
const interfaces = fs.readdirSync('./src/interfaces/');
fs.writeFile('./src/interfaces/versions.json', JSON.stringify(interfaces), (err) => {
if (err) throw err;
console.log('versions.js successfully created');
});
But this generates the same result like the object looks like. So how can I reach my goals?
Use Array#reduce and regex. This strips the file version and adds it as a key to your object and ignores anything that doesn't have a version number. It also checks if the version has _ character following immediately after.
const data = ["V1_config-interfaces.json","V2_config-interfaces.json","V3_config-interfaces.json","versions.json", "V4shouldntwork.json", "shouldntwork_V5_.json", "V123_shouldwork.json"];
const res = data.reduce((a,v)=>{
const version = v.match(/^V[1-9]+(?=_)/);
if(version === null) return a;
a[version.shift()] = v;
return a;
}, {});
console.log(res);
Pretty (very) new to Node so this is probably stupid. I have a module that defines various routes that I iterate over to define the app's routes. This works fine.
While setting up the route I want to call a function checkAvailableTranslation in another module language that checks whether a translation of that page exists in another language (req.locale being the user's favoured language that we are trying to find a match for), and if so return a URL fragment for it.
The strings object is loaded from a json file and contains an item translations that is an array of ISO country codes mapped to URL fragments.
** app.js **
var routes = require("./config/routing.js")
routes.forEach(function(item) {
item.routes.forEach(function(route) {
app.get(route.path, function(req, res) {
var strings = require(route.strings)
translations = language.checkAvailableTranslation(req.locale, strings.translations))
console.log(translations) // undefined?!?!?!?!
res.render(route.render, {
layout: route.layout,
strings: strings
})
})
})
})
** strings.translations **
[
{ "fr": "/fr" },
{ "ar": "/ar" },
{ "es": "/es" },
{ "pt": "/pt" },
{ "de": "/de" }
]
** language.js **
module.exports = function() {
var module = {}
module.checkAvailableTranslation = function(language, translations) {
translations.forEach(function(el) {
if( el[language] ) {
console.log(el[language]) // this logs a matched language
return el[language] // this disappears into the ether
}
})
}
return module
}
So everything generally behaves as expected with translations.forEach iterating successfully and finding matches. However it doesn't seem to return the value back to app.js - when I output the function's return I get undefined.
Does the fact that strings is being loaded from a json file make this an asynchronous call, as if it were a database? If so (and that would need explaining), what should I be doing here to set up a promise to deal with this?
You're returning from the forEach callback which is not possible.
You need to iterate manually over the array or write the result into a variable in the scope of your checkAvailableTranslation method(but still iterate over all items in translations).
Manual "forEach"
module.checkAvailableTranslation = function(language, translations) {
for (
var i = 0, translation = translations[i][language];
i < translations.length;
i++, translation = translations[i][language]
) {
if (translation)
return translation;
}
}
Or using Array.prototype.some
function (language, translations) {
var res;
translations.some(function (translation) {
return !!(res = translation[language]);
});
return res;
}
EDIT: Ninja approach:
function n(l,t,i){return t[i|0][l]||g(l,t,(i|0)+1)}