I have the following nested Object
const ERR_CODES = {
INVALID_CONSUMER_ID: {
code: 1000,
message: 'Invalid Consumer ID',
},
INVALID_MOBILE: {
code: 1001,
message: 'Invalid Mobile Number',
},
INVALID_ZIPCODE: {
code: 1002,
message: 'Invalid Zipcode',
},
INVALID_FIRST_NAME: {
code: 1000,
message: 'First Name',
},
}
I want to throw an error when two objects have the same code, like in the example consumer id and first name both have 1000 for error codes. What is the fastest way to go through my ERR_CODES obj and see if any code repeats?
You can use a map to keep track of the codes and compare with it, if the code is already part of the map, you could throw an error.
const ERR_CODES = {
INVALID_CONSUMER_ID: {
code: 1000,
message: 'Invalid Consumer ID',
},
INVALID_MOBILE: {
code: 1001,
message: 'Invalid Mobile Number',
},
INVALID_ZIPCODE: {
code: 1002,
message: 'Invalid Zipcode',
},
INVALID_FIRST_NAME: {
code: 1000,
message: 'First Name',
}
};
let keys = Object.keys(ERR_CODES);
let codesMap = {};
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let obj = ERR_CODES[key];
let code = obj.code;
if (!codesMap[code]) {
codesMap[code] = true
} else {
console.error('Duplicate');
}
}
Create an array of all of the codes present, then get all of the unique codes. If they are of equal length, there are no repeats. If the unique codes array is shorter, then there is a repeat.
const ERR_CODES = {
INVALID_CONSUMER_ID: {
code: 1000,
message: 'Invalid Consumer ID',
},
INVALID_MOBILE: {
code: 1001,
message: 'Invalid Mobile Number',
},
INVALID_ZIPCODE: {
code: 1002,
message: 'Invalid Zipcode',
},
INVALID_FIRST_NAME: {
code: 1000,
message: 'First Name',
},
};
const codes = Object.keys(ERR_CODES).map(err => ERR_CODES[err].code)
const uniq_codes = codes.reduce((p, c) => {
if (p.indexOf(c) < 0) p.push(c);
return p;
}, []);
console.log(codes.length == uniq_codes.length);
Since you only need to check if any code is repeated, I would choose a Set to store all of your codes:
let codes_num = Object.keys(ERR_CODES).map(elem => {
return ERR_CODES[elem].code;
});
let codes = new Set(codes_num);
Thanks to the properties of a Set, duplicated elements are just discarded. For this reason a simple check like this one:
Object.keys(ERR_CODES).length == codes.size
is going to tell you if a duplicated code has been found.
May not be the fastest way but a way to do that
const ERR_CODES = {
INVALID_CONSUMER_ID: {
code: 1000,
message: 'Invalid Consumer ID',
},
INVALID_MOBILE: {
code: 1001,
message: 'Invalid Mobile Number',
},
INVALID_ZIPCODE: {
code: 1002,
message: 'Invalid Zipcode',
},
INVALID_FIRST_NAME: {
code: 1000,
message: 'First Name',
},
};
function isValid(data) {
try{
Object.keys(data).reduce((obj, key) => {
if (data[key].code in obj) {
throw new Error(`Duplicate code: ${data[key].code}`);
} else {
obj[data[key].code] = 'found';
}
return obj;
}, {});
} catch(e) {
console.log(e.message);
return false;
}
return true;
}
console.log(isValid(ERR_CODES));
I will take the filter approach to check if the key exists more than one time.
With filter we can get a list of duplicate items by using 2 filters and checking the size of the the returned array. If the length is larger than 1, that means we found duplicates.
const ERR_CODES = {
INVALID_CONSUMER_ID: {
code: 1000,
message: 'Invalid Consumer ID',
},
INVALID_MOBILE: {
code: 1001,
message: 'Invalid Mobile Number',
},
INVALID_ZIPCODE: {
code: 1002,
message: 'Invalid Zipcode',
},
INVALID_FIRST_NAME: {
code: 1000,
message: 'First Name',
},
}
let codes = Object.values(ERR_CODES)
let result = codes.filter(item => codes.filter(i => i.code == item.code).length > 1)
console.log(result)
if(result.length > 0) {
// We have duplicates throw error here
console.error('Duplicate codes')
}
My take on the fastest solution would iterate over ERR_CODES only once and break once a duplicate is found:
const codes = new Set();
for (err in ERR_CODES) {
const {code} = ERR_CODES[err];
if (codes.has(code)) {
console.error(`Duplicate code: ${code}`);
break;
}
codes.add(code);
}
Related
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;
I am validating form from server side. once I get error message, I wants to show error message on respective textbox field's error message
client side Object
const formFields = {
firstName: {
helperText: '',
error: false
},
lastName: {
helperText: '',
error: false
},
emailID: {
helperText: '',
error: false
},
phoneNo: {
helperText: '',
error: false
},
password: {
helperText: '',
error: false
},
confirmPassword: {
helperText: '',
error: false
}
}
Server side response object after validation
const responseError = errorData.response.data.errors //below object is the response
//{
//"LastName": ["Enter LastName"],
//"FirstName": ["Enter FirstName"],
//"ConfirmPassword": ["Enter Confirm Password","confirm password do not match"]
//}
useState
const [inpValues, setInpValues] = useState(formFields)
Conditions to update
if ClientSideObj.key === responseObj.key then setInpValues of error and helperText field
const responseError = errorData.response.data.errors
console.log(responseError)
var FormFieldName = ""
for (keys in formFields) {
console.log('FormField keys = ' + keys)
for (var errorKeys in responseError) {
if (keys.toLowerCase() === errorKeys.toLowerCase()) {
console.log('* MATCHED FIELDS = ' + errorKeys)
//Matched 3 fields(LastName,FirstName,ConfirmPassword) successfully
FormFieldName = keys
setInpValues(prevInpValues => ({
...prevInpValues,
[FormFieldName]: {
...prevInpValues[FormFieldName],
error: true,
helperText: responseError[errorKeys]
}
})
)
console.table(inpValues)
}
}
}
Note
I go through this stack overflow already, then I passed previousState values also. still result same.
It's updating only the last for loop condition value
If the response.error object has return only one field, then it's updating that one
It's updating only the last for loop condition value
Javascript infamous Loop issue?
What is the difference between "let" and "var"?
as Miguel Hidalgo states, you should make all changes in one update:
const responseError = errorData.response.data.errors
console.log(responseError)
setInpValues(state => {
const newState = { ...state };
for (let errorKey in responseError) {
for (let formField in formFields) {
// this would be so much simpler if your properties would be in the correct case and you'd not have to do this dance with `.toLowerCase()`
if (formField.toLowerCase() !== errorKey.toLowerCase()) {
continue;
}
newState[formField] = {
...newState[formField],
error: true,
helperText
}
}
}
return newState
});
If errorKey and fieldName would be identical and you'd not have to match them case insensitive you could write this:
const responseError = errorData.response.data.errors
console.log(responseError)
setInpValues(state => {
const newState = { ...state };
for (let formField in responseError) {
newState[formField] = {
...newState[formField],
error: true,
helperText: responseError[formField]
}
}
return newState
});
What you should do in order to avoid unnecessary extra renders its to loop inside setState callback:
setInpValues(prevInpValues => {
for (const formKey in formFields) {
for (const errorKey in responseError) {
if (formKey.toLowerCase() === errorKey.toLowerCase()) {
prevInpValues[formKey] = {
...prevInpValues[formKey],
error: true,
helperText: responseError[errorKey]
}
}
}
}
return {...prevInpValues}
}
Hi I am trying to figure out the best way to achieve something. I essentially receive a lot of data like so
[
{
name: 'email.delivered',
programme: 'Email One',
timestamp: 2022-03-24T18:06:02.000Z,
"payload":{
"id":"bcabca7c-a5d5-4e02-b247-2292240ffc77",
},
},
{
name: 'interaction.click',
programme: 'Email One',
timestamp: 2022-03-24T18:06:02.000Z,
"payload":{
"correlation":{
"channel":"email",
"messageId":"bcabca7c-a5d5-4e02-b247-2292240ffc77",
},
"metadata":{
"customerId":"9999999111",
"programName":"Email One"
}
},
},
...
]
The name is the event, can be delivered, failed, expired or click. The programme is what I want things categorised by. So I am doing some checks beforehand, and my code (removed a bit for simplicity) at the moment is like so
emails.forEach((record) => {
const data = JSON.parse(record.data);
if (data?.name === 'email.delivered') {
const id = data?.payload?.id;
if (!deliverDates[id]) deliverDates[id] = record.timestamp;
deliveredMap.set(programme, (deliveredMap.get(programme) || 0) + 1);
return;
}
if (data?.name === 'email.click') {
const id = data?.payload?.correlation?.messageId;
if (id) {
const deliveryDate = new Date(deliverDates[id]);
if (deliveryDate.getTime() > Date.now() - 1209600000) {
const programme = record?.programme;
clicksMap.set(programme, (clicksMap.get(programme) || 0) + 1);
}
}
}
});
The problem with the above is now I now have two Maps, when really I want just one Object returned with the programme as the key. For each programme I want to count all the different event types. So what I would like to see is something more like this
{
'Email One': {
delivered: 315,
failed: 18,
expired: 14,
click: 27,
},
'Email Two': {
delivered: 542,
failed: 322,
expired: 33,
click: 22,
}
...
}
How can I achieve this?
Thanks
Use a helper function to record the event occurence:
const eventCountsByProgramme = {};
function recordOccurence(programme, event) {
const eventCounts = (eventCountsByProgramme[programme] ??= {});
eventCounts[event] = (eventCounts[event] ?? 0) + 1;
}
Then use
recordOccurence(programme, 'delivered');
instead of
deliveredMap.set(programme, (deliveredMap.get(programme) || 0) + 1);
and
recordOccurence(programme, 'click');
instead of
clicksMap.set(programme, (clicksMap.get(programme) || 0) + 1);
I am using json-rule-engine .
https://www.npmjs.com/package/json-rules-engine
I am having a student list which have name and their percentage, Also I have business rule the percentage should be greater thank or equal to than 70 . so I want to print all students name those have percentage more than 70
here is my code
https://repl.it/repls/AlienatedLostEntropy#index.js
student list
const students = [
{
name:"naveen",
percentage:70
},
{
name:"rajat",
percentage:50
},
{
name:"ravi",
percentage:75
},
{
name:"kaushal",
percentage:64
},
{
name:"piush",
percentage:89
}
]
rule
engine.addRule({
conditions: {
all: [
{
fact: "percentage",
operator: "greaterThanInclusive",
value: 70
}
]
},
onSuccess(){
console.log('on success called')
},
onFailure(){
console.log('on failure called')
},
event: {
type: "message",
params: {
data: "hello-world!"
}
}
});
code
https://repl.it/repls/AlienatedLostEntropy#index.js
any update
The json-rules-engine module takes data in a different format. In your Repl.it you have not defined any facts.
Facts should be:
let facts = [
{
name:"naveen",
percentage:70
},
[...]
Also, the module itself doesn't seem to process an array of facts. You have to adapt it to achieve this. This can be done with:
facts.forEach((fact) => {
engine
.run(fact)
[...]
Finally, the student data is found inside the almanac. You can get these values with: results.almanac.factMap.get('[name|percentage|age|school|etc]').value
Here is the updated Repl.it: https://repl.it/#adelriosantiago/json-rules-example
I might have submitted a completely unrelated answer, but here goes. Since the students object is an array, you could just loop through it and then use an if else statement.
for (let i = 0; i < students.length; i++) {
if (students[i].percentage >= 70) {
console.log(students[i].name);
}
}
Sorry if this is incorrect!
Here is a working example.
Counting success and failed cases
const { Engine } = require("json-rules-engine");
let engine = new Engine();
const students = [
{
name:"naveen",
percentage:70
},
{
name:"rajat",
percentage:50
},
{
name:"ravi",
percentage:75
},
{
name:"kaushal",
percentage:64
},
{
name:"piush",
percentage:89
}
]
engine.addRule({
conditions: {
all: [{
fact: 'percentage',
operator: 'greaterThanInclusive',
value: 70
}]
},
event: { type: 'procedure_result'}
})
let result = {success_count : 0 , failed_count : 0}
engine.on('success', () => result.success_count++)
.on('failure', () => result.failed_count++)
const getResults = function(){
return new Promise((resolve, reject) => {
students.forEach(fact => {
return engine.run(fact)
.then(() => resolve())
})
})
}
getResults().then(() => console.log(result));
I am having difficulty getting my for loop to add a new key-value pair to an object. It has no problem changing a current value of a key that already exists but for some reason, it will not add a new one
async function test(messagesWithSomeContent) {
for (i = 0; i < messagesWithSomeContent.length; i++) {
messagesWithSomeContent[i]["photo"] = 'please add this'; // this does not add a new key value pair
messagesWithSomeContent[i]["sender"] = 'change this'; // this works
console.log(messagesWithSomeContent[i]);
}
return win(await Promise.all(messagesWithSomeContent));
}
async function win(yay) {
console.log('yay');
}
messageWithSomeContent
[ { _id: 5e8f5a6a2582bf629998c3fe,
sender: '5e8f8d6be541b07ab8d8770b',
content: { content: 'Welcome to' },
__v: 0 },
{ _id: 5e8f594768fdda61d4f2ef6d,
sender: '5e8f86852c2a5174f3ca5e8c',
content: { content: 'hello test' },
__v: 0 },
{ _id: 5e8f585ee3eaa06136048b5c,
sender: '5e8f883627154676347fe286',
content: { lol: 'yeesh' },
__v: 0 } ]
I looked at some similar posts and their solutions are not working.
i can't add comment to you so i want to comment on this answer
what the output you want ?
async function test(messagesWithSomeContent) {
for (i = 0; i < messagesWithSomeContent.length; i++) {
messagesWithSomeContent[i]["photo"] = 'please add this';
messagesWithSomeContent[i]["sender"] = 'change this';
}
return win(await Promise.all(messagesWithSomeContent));
}
async function win(yay) {
const u = await yay; // tried add this to see the result
console.log(u)
}
tried console.log on here => https://repl.it/repls/RealisticExtralargeAstronomy
With the limited code added here, Try using the spread operator, although using JSFiddle I was able to get yours to work with my own made up sample data.
async function test(messagesWithSomeContent) {
for (i = 0; i < messagesWithSomeContent.length; i++) {
messagesWithSomeContent[i] = Object.assign({}, messagesWithSomeContent[i], {photo: 'please add this', sender: 'change this'})
console.log(messagesWithSomeContent[i]);
}
return win(await Promise.all(messagesWithSomeContent));
}
Promise.all must receive an array of promises. A way of doing this is using map over the array of parameters, calling the function on the callback.
Promise.all(parameters.map(parameter => myAsyncFunction(parameter)));
var messages = [
{_id: '5e8f5a6a2582bf629998c3fe', sender: '5e8f8d6be541b07ab8d8770b', content: { content: 'Welcome to' }, __v: 0},
{_id: '5e8f594768fdda61d4f2ef6d', sender: '5e8f86852c2a5174f3ca5e8c', content: { content: 'hello test' }, __v: 0},
{ _id: '5e8f585ee3eaa06136048b5c', sender: '5e8f883627154676347fe286', content: { lol: 'yeesh' }, __v: 0}];
async function test() {
for (var i = 0; i < messages.length; i++) {
messages[i]["photo"] = 'please add this';
messages[i]["sender"] = 'change this';
}
var result = await Promise.all(messages.map(el => win(el)));
console.log(result); // Should contain an array with all responses
}
async function win(param) {
console.log(param);
return param.photo;
}
test();