looping a javascript object inside a loop of custom object - javascript

First of all I'm beginner in javascript. I have this data data.responseJSOn.errors -
Currently I'm displaying the errors like this way -
var errors = data.responseJSON.errors;
console.log(errors);
errors.title &&
errors.title.forEach(function(messageText) {
displayError(messageText);
});
errors.message &&
errors.message.forEach(function(messageText) {
displayError(messageText);
});
How can I display errors from there by using single code instead of predefined errors.title or errors.message.
expected code like -
var errors = data.responseJSON.errors;
var list = ["title", "message"];
list.forEach(function(item) {
errors.item &&
errors.item.forEach(function(messageText) {
displayError(messageText);
});
});
How can I fix it to get output.

You can try something more generic regardless of the key names in object data.responseJSON.errors
var errors = data.responseJSON.errors;
var list = ["title", "message"]; // valid keys for which we need to show message
for (key in errors) {
// check if key is valid and is array
if(list.includes(key) && Array.isArray(errors[key])) {
errors[key].map((msg) => {
displayError(msg);
})
}
}

In the case you're going to need the [] syntax to access a field of the errors object with the value of a variable:
var errors = data.responseJSON.errors;
var list = ["title", "message"];
list.forEach(function(item) {
errors[item] &&
errors[item].forEach(function(messageText) {
displayError(messageText);
});
});

You can access the respective property using []:
var errors = data.responseJSON.errors;
var list = ["title", "message"];
list.forEach(function(item) {
errors[item] &&
errors[item].forEach(function(messageText) {
displayError(messageText);
});
});
Otherwise (and as a better alternative), you can use the for...in loop syntax to access enumerable object properties:
var errors = data.responseJSON.errors;
var list = ["title", "message"];
errors.forEach(function(error) {
for (property in error) {
displayError(error[property]);
}
});

here you don't have to explicitly mention the type of keys the error message has. Even in future, if messages and title changes you don't have to modify the code again.
const messageText = (message: string) => {
console.log('message', message)
}
Object.keys(response).map((error) => response[error].length && response[error].forEach((message: string) => messageText(message)));

Related

Simplify forEach in forEach React

I have a function where I have to return for each "subcontractor" its response for each selection criteria.
Subcontractor object contains a selectionCriteria object. selectionCriteria object contains an array of data for each selectionCriteria a user has responded to.
Each array item is an object, that contains files, id, request (object that contains info about selection criteria user is responding to), response (contains value of the response).
Here is an example of how a subcontractor looks:
This is the function I come up with, but it's quite complex:
const { subcontractors } = useLoaderData<typeof loader>();
const { t } = useTranslation();
const submittedSubcontractors = subcontractors.filter(
(s) => s.status === 'submitted'
);
const subcontractorsResponsesToSelectionCriteria: Array<ISubcontractor> = [];
let providedAnswersResponded: boolean | null = null;
let providedAnswersFiles: Array<IFile> | [] = [];
let providedAnswersRequiresFiles: boolean | null = null;
submittedSubcontractors.forEach((u) => {
u.selectionCriteria.forEach((c) => {
if (c.request.id === criteriaId) {
if (c.response && 'answer' in c.response) {
if (typeof c.response.answer === 'boolean') {
providedAnswersResponded = c.response.answer;
} else {
providedAnswersResponded = null;
}
} else {
providedAnswersResponded = null;
}
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u as ISubcontractor);
}
});
});
How could I simplify this code by using .reduce() method, or maybe even better ideas?
You should start working on reducing the level of nesting in your if/else like so:
function getProvidedAnswersResponded(response: any) {
if (response && ('answer' in response) && (typeof response.answer === 'boolean')) {
return response.answer;
}
return null;
}
submittedSubcontractors.forEach(u => {
u.selectionCriteria.forEach(c => {
if (c.request.id !== criteriaId) {
return;
}
providedAnswersResponded = getProvidedAnswersResponded(c.response);
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u);
});
});
The strategy followed was basically to invert the special cases (such as c.requet.id === criteriaId) and exit the function immediately.
Also, extracting the "provided answer responded" function seems atomic enough to move it to a separate block, giving it more verbosity about what that specific code block is doing.

TypeError: null is not an object (evaluating 'arr.push')

Almost a similar question was asked earlier but I could't figure it out in my case so here it is (I'm using react-native and expo):
I was using chrome to see the result of my codes for a to-do app and it was working well until I wanted to try it in my phone that I faced this once the "addTodo" function was called:
TypeError: null is not an object (evaluating 'arr.push')
this is the code:
addTodo = () => {
var newTodo = this.state.text;
var arr = this.state.todo;
if (!newTodo) {
alert("Empty!");
} else {
arr.push(newTodo);
this.setState({ todo: arr, text: "" });
this.setDataLocally();
}
"text" contains the string coming from the input and will be added to "todo" array.
as you can see the "arr.push" should only be called when "newTodo" is not null or empty.
I only get the error when I want to use the Asyncstorage. this is the what should be called at the end of above code:
setDataLocally = () => {
var jsonData = JSON.stringify(this.state.todo);
AsyncStorage.setItem("list", jsonData);
What should I do? thanks in advance!
The problem is that your variable is null .. but it expects a list.
Try this:
addTodo = () => {
var newTodo = this.state.text;
var arr = this.state.todo !== null ? this.state.todo : [];
if (!newTodo) {
alert("Empty!");
} else {
arr.push(newTodo);
this.setState({ todo: arr, text: "" });
this.setDataLocally();
}

Storing a dynamic list using chrome.storage.sync.set() for a Chrome extension

I am trying to store an object, mapping a string to a list, using chrome.sync.get. My goal is to create a new list for a non-existing key or append an element to the list if the key exists. However, I am unable to populate the object. When I try to retrieve the values I have previously inserted, I get an empty Object as the returned value. Following is the code I am using:
let currentTabId = '234';
let spawnedTabId = '390';
chrome.storage.sync.get(currentTabId, function(data) {
if (typeof data.currentTabId === 'undefined') {
chrome.storage.sync.set({currentTabId: [spawnedTabId]}, function() {
console.log("Initialized "+currentTabId+" with "+spawnedTabId);
});
chrome.storage.sync.get(currentTabId, function(data) {
console.log(data);
});
} else {
data.currentTabId.push(spawnedTabId)
chrome.storage.sync.set({currentTabId: data.currentTabId}, function() {
console.log("Appended "+spawnedTabId+" to "+currentTabId);
});
}
});
The output I am getting is:
>>> Initialized 234 with 390
>>> {}
__proto__: Object
The code had three mistakes:
incorrect use of a variable to make an object literal,
instead of {variable: value} it should be {[variable]: value}, more info
incorrect use of a variable to read a property from an object,
instead of obj.variable it should be obj[variable]
incorrect use of asynchronous API,
the data should be read after it's written i.e. inside the callback.
let key = '234';
let spawnedTabId = '390';
chrome.storage.sync.get(key, data => {
const spawned = data[key] || [];
spawned.push(spawnedTabId);
chrome.storage.sync.set({ [key]: spawned }, () => {
// now you can read the storage:
// chrome.storage.sync.get(key, console.log);
});
});

TypeError: Cannot set property 'employees' of undefined

I'm setting up a searchbar in a web app, and I've got most of it done(i.e. it's successfully finding the objects that I'm looking for), however, a dispatch at the end of the searchbar code. This is being built using Javascript, React and Redux. I'm entirely new to react, and my teams React guy is off for the week, so any help would be a massive help.
searchInList = (e) => {
const { dispatch, employees } = this.props;
const valueSearched = e.target.value;
let currentList = [];
let newList = [];
if (valueSearched !== ' ') {
currentList = employees;
currentList.map(employeeObject => {
Object.values(employeeObject).filter(item => {
let itemString;
if (typeof (item) != 'string') {
itemString = JSON.stringify(item);
} else {
itemString = item;
}
let lc = itemString.toLowerCase();
const filter = valueSearched.toLowerCase();
if (lc.includes(filter)) {
if (!newList.includes(employeeObject)) {
newList.push(employeeObject);
}
}
});
});
} else {
newList = employees;
}
console.log(newList);
dispatch(onChangeEmployee('employees', newList));
};
This should just narrow down the amount of objects being displayed (within the search terms), but it crashes and throws up this error: "TypeError: Cannot set property 'employees' of undefined".
It crashes on the dispatch(onChangeEmployee('employees', newList)); line
From
Cannot set property 'employees' of undefined
I can see you do something like
var a
a.employees='hello'
However, you never refer employees property of an object in the snippet presented
So the chance is high that the error is from onChangeEmployee

array.forEach works, but not when I nest another inside

I've got two pages I'm working on, and both return an array of objects. When I use the following code, the new results work:
this.adminService.waiversGetAll()
.subscribe((data: Waiver[]) => {
this.waivers = data;
this.waivers.forEach((e) => {
if(e.has_signed === true) {
e.url = `View`
} else {
e.url = `${e.message}`;
}
return e;
});
console.log(this.waivers);
})
}
But when I try to do the same thing with a different array (where I need to update the values of an array nested inside) I don't get updated values:
this.adminService.GetUnsignedWaivers()
.subscribe((data: Player[]) => {
console.log("data",data);
data.forEach(e => {
let record: Object = {};
for(let i = 0; i < e.waivers.length; i++) {
console.log(e.waivers[i].has_signed);
if (e.waivers[i].has_signed === true) {
e.waivers[i].url = e.waivers[i].signatureUrl;
console.log(e.waivers[i].url);
e.waivers[i].message = "View Waiver";
} else {
e.waivers[i].url = e.waivers[i].url;
e.waivers[i].message = e.waivers[i].message;
}
console.log(e.waivers[i].message);
return;
};
return e;
});
this.size = this.players.length;
console.log(this.players);
})
}
When I look at the console.log of e.waivers[i].has_signed, the data is correct, but after that it's not right.
What do I have to do to make this work? I've tried using a for loop inside the foreach, and a bunch of other stuff.
The data supplied to the loop provides info like:
{
buyer: "email#someaddress.edu"
event: "COED A"
field: "Main"
net: null
player: {shirtSize: null, avp_id: 12345678, adult: true, …}
team: null
waivers: [{
email: "someemail#gmail.com",
has_signed: true,
message: "Liability Waiver",
signatureUrl: "https://somelink.pdf",
url: "https://somelink.com/somekeyidentifier"
}
IF the player has signed the waiver, there will be a signatureUrl field and the message should say "View Waiver" instead of the message telling me what type of waiver they will sign. I want the url to be set to signatureUrl if they signed, so I can use it in a table that doesn't like manipulation of data.
A visual of what is returned in my table:
All I get is 1600 records showing the url as though everyone hasn't signed, but when I console.log has_signed in the inner loop, it's showing TRUE for the ones that should show a signatureUrl instead.
Quickly looking at it, you have a return statement within your for loop, which would stop it from running after the first iteration.
First of all drop all the return statements in your code. Next, use map instead of forEach as the former returns you the new manipulated array and the latter is used just for iteration purpose.
Your code within subscribe then becomes:
data.waivers = data.waivers.map((waiver) => {
if (waiver.has_signed) {
// your logic goes here...
waiver.url = waiver.signatureUrl;
waivers.message = "View Waiver";
}
// No else is required as you are just reassigning with same values
});
this.playerDetails = data;
At last bind this modified data in your template.

Categories