Inconsistent results sending object for translation - javascript

I am working on a project that requires internationalisation using AWS for translation. I currently store translations in objects that are recursively
looped through, each string being sent off for translation and then re-added to the object. I need to leave the object key untranslated.
However, I am experiencing inconsistent results, most of the time the full object is returned translated, however sometimes there are values missing,
and the ones that are missing are inconsistent as well, sometimes there, sometimes not.
I think the issue may be to do with sending hundreds of requests to the API in a short space of time.
QUESTION:
Any ideas what the issue could be?
What is the best way for translating an object using AWS Translate API?
Is there a way to send the entire object off at once keeping keys un-mutated?
Below is my code for translation:
const translateText = async ({ text = '', sourceLang, targetLang }) => {
if (!targetLang || !sourceLang) {
throw new Error('Missing source or target lang');
}
const params = {
SourceLanguageCode: sourceLang,
TargetLanguageCode: targetLang,
Text: text,
};
try {
const translationData = await translateAWS.translateText(params).promise();
return translationData.TranslatedText;
} catch (error) {
throw new Error('translateText API error :>> ', error);
}
};
const translateJSON = async ({
obj,
targetLang,
sourceLang,
displayLang = true,
}) => {
const response = {};
for (const key in obj) {
let word = '';
try {
if (typeof obj[key] === 'object') {
word = await translateJSON({
obj: obj[key],
targetLang,
sourceLang,
displayLang: false,
});
} else {
word = await translateText({ text: obj[key], sourceLang, targetLang });
}
} catch (error) {
Sentry.captureException('translateJSON API error:', error);
word = '';
}
if (displayLang) {
response[targetLang] = response[targetLang] || {};
response[targetLang][key] = word;
} else {
response[key] = word;
}
}
return response;
};
Example object to translate:
const common = {
buttons: {
readMore: 'Read more',
seeAdvice: 'See advice',
goBack: 'Go back',
accessibility: 'Accessibility',
decreaseTextSize: '- Decrease text size',
increaseTextSize: '+ Increase text size',
seeMore: 'See more',
seeLess: 'See less',
addATip: 'Add a tip',
addAnotherTip: 'Add another tip',
addColourOverlay: 'Add colour overlay',
},
words: { and: 'and' },
placeholders: { select: 'Select...' },
heading: {
shareThisPage: 'Share this page',
helpfulResources: 'Helpful resources',
},
section: {
subSection: {
description:
'So we can show you the best information, which one of these best describes you?',
},
changeLanguage: {
title: 'Change language',
placeholder: 'Search',
},
helpMe: {
title: 'Help!',
subtitle: 'Text here',
},
helpBudget: {
title: 'Need help with budgeting?',
},
colors: [
{
label: 'Blue',
},
{
label: 'Green',
},
{
label: 'Yellow',
},
{
label: 'Red',
},
],
};
export default common;

Related

How can I prompts loop in Node.js?

I want the main.js loop to be re-selectable when the user has selected option.
Here is the code.
main.js
const prompts = require('prompts');
var option = require('./option.js');
(async () => {
const response = await prompts({
type: 'select',
name: 'value',
message: 'choice',
choices: [
{ title: 'option1', description: 'option1 description', value: '1' },
{ title: 'exit', description: 'exit from script',value: '0' }
],
initial: 1
}
).then(response => {
if(response.value == 1){
console.info('you select option1');
option.option1()
// i want to run main.js again here
}
else {
console.info('you select exit');
// i want to exit from main.js
}
});
})();
option.js
module.exports = {
option1:function() {
console.log("You have selected option 1")
}
}
When the user has selected option1, after the option.js script's function is done, I would like it to return to the new options page.
I've tried many ways but I still can't find a way. What can I try?
Is this the solution to your problem? Also notice when using await, dont use then.
const prompts = require('prompts');
var option = require('./option.js');
async function prompt () {
const response = await prompts({
type: 'select',
name: 'value',
message: 'choice',
choices: [
{ title: 'option1', description: 'option1 description', value: '1' },
{ title: 'exit', description: 'exit from script',value: '0' }
],
initial: 1
})
return response.value === '1'?option.option1() & prompt():false
}
prompt()

nodejs filtering an array of objects where the filtering is partially done in an async function

I've read many similar questions and have tried a bunch of code. Unfortunately, I'm not getting my code to run :-(
So, the situation is as follows: In a route of a node.js server, I have to respond with a filtered array of Objects. Unfortunately, whatever I do, I always get an empty array [] back. The filter is a bit tricky in my opinion, as it consists of a string comparison AND an async call to a library function. With the console output, I can clearly see that the correct element is found, but at the same time I see that I've already received the object...
Here is some code that exemplifies my challenge:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
// code from a library. Can't take an influence in it.
async function booleanWhenGood(id) {
if (id in some Object) {
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
// Should return only elements with type 'ofInterest' and that the function booleanWhenGood is true
router.get('/', function(res,req) {
tryOne(testArray).then(tryOneResult =>{
console.log('tryOneResult', tryOneResult);
});
tryTwo(testArray).then(tryTwoResult => {
console.log("tryTwoResult ", tryTwoResult);
});
result = [];
for (const [idx, item] of testArray.entries() ) {
console.log(idx);
if (item.data.someDoc.type === "ofInterest") {
smt.find(item.id).then(element => {
if(element.found) {
result.push(item.id);
console.log("ID is true: ", item.id);
}
});
}
if (idx === testArray.length-1) {
// Always returns []
console.log(result);
res.send(result);
}
}
})
// A helper function I wrote that I use in the things I've tried
async function myComputeBoolean(inputId, inputBoolean) {
let result = await booleanWhenGood(inputId)
if (result.myBoolean) {
console.log("ID is true: ", inputId);
}
return (result.myBoolean && inputBoolean);
}
// A few things I've tried so far:
async function tryOne(myArray) {
let myTmpArray = []
Promise.all(myArray.filter(item => {
console.log("item ", item.id);
myComputeBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
tmpjsdlf.push(item.id);
return true;
}
})
})).then(returnOfPromise => {
// Always returns [];
console.log("returnOfPromise", myTmpArray);
});
// Always returns []
return(myTmpArray);
}
async function tryTwo(myArray) {
let myTmpArray = [];
myArray.forEach(item => {
console.log("item ", item.id);
myCompuBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
myTmpArray.push(item.did);
}
})
});
Promise.all(myTmpArray).then(promiseResult => {
return myTmpArray;
});
}
Asynchronous programming is really tough for me in this situation... Can you help me get it running?
I didn't inspect your attempts that closely, but I believe you are experiencing some race conditions (you print return and print the array before the promises resolve).
However you can alwayd use a regular for loop to filter iterables. Like this:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
async function booleanWhenGood(id) {
if (id in { 'stringId1': 1, 'stringId2': 1 }) { // mock object
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
async function main() {
let filtered = []
for (item of testArray)
if ((await booleanWhenGood(item.id)).myBoolean && item.data.someDoc.type === 'ofInterest')
filtered.push(item)
console.log('filtered :>> ', filtered);
}
main()

Vue js: mapping array from API response data to checkbox list and back

I'm using Vue js to display and edit details of a person. The person being edited has a list of favourite colours that I want to display as a list of checkboxes. When I change the colours selected, and click the 'Update' button, the person object should be updated accordingly, so I can pass back to the api to update.
I've got as far as displaying the Person object's colours correctly against their respective checkboxes. But I'm struggling with passing the changes to the colour selection, back to the Person object. Below is my checkbox list and details of how I've tried to implement this. Is there a better way of doing this?
I've tried using 'b-form-checkbox-group'. Below is my code.
Please note - The list of available colours is dynamic, but I've temporarily hardcoded a list of colours ('colourData') till I get this working.
Also, in the 'UpdatePerson' method, I've commented out my attempts to get the selected colours mapped back to the Person object.
<template>
<form #submit.prevent="updatePerson">
<b-form-group label="Favourite colours:">
<b-form-checkbox-group id="favColours"
v-model="colourSelection"
:options="colourOptions"
value-field="item"
text-field="name">
</b-form-checkbox-group>
</b-form-group>
<div class="container-fluid">
<b-btn type="submit" variant="success">Save Record</b-btn>
</div>
</form>
</template>
<script>
import service from '#/api-services/colours.service'
export default {
name: 'EditPersonData',
data() {
return {
personData: {
personId: '',
firstName: '',
lastName: '',
colours:[]
},
colourData: [
{ colourId: '1', isEnabled: '1', name: 'Red' },
{ colourId: '2', isEnabled: '1', name: 'Green' },
{ colourId: '3', isEnabled: '1', name: 'Blue' },
],
selectedColours: [],
colourSelection: []
};
},
computed: {
colourOptions: function () {
return this.colourData.map(v => {
let options = {};
options.item = v.name;
options.name = v.name;
return options;
})
}
},
created() {
service.getById(this.$route.params.id).then((response) => {
this.personData = response.data;
this.colourSelection = response.data.colours.map(function (v) { return v.name; });
this.selectedColours = response.data.colours;
}).catch((error) => {
console.log(error.response.data);
});
},
methods: {
async updatePerson() {
//const cs = this.colourSelection;
//const cd = this.colourData.filter(function (elem) {
// if (cs.indexOf(elem.name) != -1) { return elem;}
//});
//this.personData.colours = [];
//this.personData.colours = cd;
service.update(this.$route.params.id, this.personData).then(() => {
this.personData = {};
}).catch((error) => {
console.log(error.response.data);
});
},
}
}
</script>
Any help wold be much appreciated.
Thanks
I got this working by making the below changes to the commented part in the 'updatePerson()' method:
methods: {
async updatePerson() {
const cs = this.colourSelection;
const cd = this.colourData.filter(function (elem) {
if (cs.some(item => item === elem.name)) { return elem; }
});
this.personData.colours = [];
this.personData.colours = cd;
service.update(this.$route.params.id, this.personData).then(() => {
this.personData = {};
}).catch((error) => {
console.log(error.response.data);
});
}
}

Deleting JSON object based on attribute

I am working with React, I have got a variable named newtreeData, which looks like:
var newTreeData = {
name: submitted_title,
resource_link: submitted_resource_link,
details: submitted_details,
uuid: submitted_uuid,
website_image: submitted_website_img,
children: [
{
name: "Edit and save",
resource_link: "uh",
uuid: uuid.v4(),
details: "hi",
website_image:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
children: [{...}, {}, ...]
},
{
name: "Edit and save",
resource_link: "uh",
uuid: uuid.v4(),
details: "hi",
website_image:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png"
}
]
};
The line children: [{...}, {}] is just representing that newTreeData's children can have children which can have children...
Anyways, I wrote a method name findUUIDthenDelete which should do in pseudocode: if(object.uuid == toFindUUID) then delete object, and here's the full code for findUUIDthenDelete:
findUUIDthenDelete = (orig_data, to_delete_uuid) => {
var targetIsFound = false;
if (orig_data.uuid == to_delete_uuid) {
targetIsFound = true;
}
if (targetIsFound == false) {
if (orig_data.children === undefined) {
} else {
//if target not found, run recursion
orig_data.children.map(eachChildren =>
this.findUUIDthenDelete(eachChildren, to_delete_uuid)
);
}
} else {
console.log(orig_data, "this is the child ");
console.log(orig_data.parent, "is found, deleting its parent");
delete orig_data
}
};
As you can see this method is two parts: first I locate the object which has the uuid that we are trying to seek (potentially with some recursions), then delete the object. However, right now I am getting the "delete in local variable strict mode blah blah" error because of doing delete orig_data. Any insights to any workarounds to that error or some totally new way of tackling this? Also sincere apologies if there is an obvious solution I am out of mental energy and unable to think of anything algorithmic at the moment.
This should do it:
function findUUIDthenDelete(tree, uuid) {
if (!tree.children) return;
tree.children = tree.children.filter(c => c.uuid !== uuid);
tree.children.forEach(c => findUUIDthenDelete(c, uuid));
}
Should be pretty self-explanatory.
First, if the current node has no children, exit right away.
Next, potentially remove a child from the children array if the uuid matches using filter().
Finally, recursion.
Ok I'll admit this turned out to be more complicated than I thought, but the solution below will work if you can use Immutable. Essentially it walks your objects and collects the path to find the object that has the uuid and then once it has done that, it removes it.
const testMap = Immutable.fromJS({
uuid: 1,
children: [{
uuid: 2,
children: [{
uuid: 3,
children:[{
uuid: 8
}]
},
{
uuid: 4
},
{
uuid: 5
},
]
},
{
uuid: 7
}]
});
function findPath(checkMap, uuid, pathMap, currentIndex) {
if (checkMap.has('uuid') && checkMap.get('uuid') === uuid) {
const updatePathMap = pathMap.get('path').push(currentIndex);
return new Immutable.Map({
found: true,
path: pathMap.get('path').push(currentIndex)
});
} else {
if (checkMap.has('children') && checkMap.get('children').size > 0) {
for (let i = 0; i < checkMap.get('children').size; i++) {
const child = checkMap.get('children').get(i);
const checkChildPath = findPath(child, uuid, pathMap, i);
if (checkChildPath.get('found') === true) {
let updatePath = checkChildPath.get('path').push('children');
updatePath = updatePath.push(currentIndex);
return new Immutable.Map({
found: true,
path: updatePath
});
}
}
}
return pathMap;
}
}
const testPath = findPath(testMap, 7, new Immutable.Map({
found: false,
path: new Immutable.List()
}), 0);
console.info(testPath);
const testPath2 = findPath(testMap, 8, new Immutable.Map({
found: false,
path: new Immutable.List()
}), 0);
console.info(testPath2);
if (testPath2.get('found') === true) {
const path = testPath2.get('path');
if (path.size === 1 && path.get(0) === 0) {
// Your highlest level map has the uuid
} else {
const truePath = path.shift();
const cleanedUpMap = testMap.removeIn(truePath);
console.info(cleanedUpMap);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>

How can I reduce cyclomatic complexity from this piece of code?

I'm refactoring some legacy code. I get error from jshint about cyclomatic complexity which I'm trying to figure out how to fix the warning. The code is in node.js so anything in JavaScript is very much welcome.
if (rawObj.title) {
formattedObj.name = rawObj.title;
}
if (rawObj.urls && rawObj.urls.web) {
formattedObj.url = rawObj.urls.web.project;
}
if (rawObj.photo) {
formattedObj.image = rawObj.photo.thumb;
}
if (rawObj.category) {
formattedObj.category = rawObj.category.name;
}
It's really just checking if the property exists and map to a new object.
Kind of late to the party but you (or others looking for ways to reduce cyclomatic-complexity) could go with an approach like this. It's kind of like the strategy pattern. Depending if you can or can't use ES6, will determine which setRawObjProp you should use.
function setFormObjName () {
formattedObj.name = rawObj.title;
console.log(arguments.callee.name,formattedObj);
}
function setFormObjURL () {
formattedObj.url = rawObj.urls.web.project;
console.log(arguments.callee.name,formattedObj);
}
function setFormObjImage () {
formattedObj.image = rawObj.photo.thumb;
console.log(arguments.callee.name,formattedObj);
}
function setFormObjCat () {
formattedObj.category = rawObj.category.name;
console.log(arguments.callee.name,formattedObj);
}
function setRawObjProp(obj) {
var objectMap = new Map();
objectMap
.set('string1', setFormObjName)
.set('string2', setFormObjURL)
.set('string3', setFormObjImage)
.set('string4', setFormObjCat);
if (objectMap.has(obj)) {
return objectMap.get(obj)();
}
else {
console.log('error', obj);
}
}
/*
function setRawObjProp2(obj) {
var objectMap = {
'string1': setFormObjName,
'string2': setFormObjURL,
'string3': setFormObjImage,
'string4': setFormObjCat,
};
if (objectMap.hasOwnProperty(obj)) {
return objectMap.get(obj)();
}
else {
console.log('error', obj);
}
}
*/
var rawObj = {
title: 'string1',
urls: {
app: {
project: 'some thing'
},
web: {
project: 'string2'
}
},
photo: {
large: 'large',
thumb: 'string3'
},
category: {
name: 'string4',
type: 'some type',
id: 12345
}
},
formattedObj = {
title: '',
urls: {
web: {
project: ''
}
},
photo: {
thumb: ''
},
category: {
name: ''
}
};
setRawObjProp('string1');
/* setRawObjProp2('string1') */

Categories