How to forEach where (angular.js) - javascript

In this forEach I push some fields into an existing array.
How can i filter out where prop.isRequired = false?
So: (only) Loop everything in schema.properties, where isRequired = true;
angular.forEach(vm.schema.properties, function (prop,key) {
vm.mappingFields.push({ //this is an array
source: null, //this gets pushed
destination: key, //this gets pushed
fieldType: prop.type, //this gets pushed
isRequired: prop.isRequired, //this gets pushed
});
});

I'd do it in modern way like this:
vm.mappingFields = vm.schema.properties.filter({ isRequired } => isRequired).map(prop => {
source: null,
destination: key,
fieldType: prop.type,
isRequired: prop.isRequired
})
First we use ES6 Array.filter method then just Array.map to generate new array with needed fields and assign new generated array into vm.mappingFields.
Also I used ES6 Destructuring { isRequired } => isRequired to reduce code (prop => prop.isRequired) and make it more easy to read.
One more thing is when you generate new array the isRequired: prop.isRequired is unnecessary since we know that only elements with isRequired: true came here.. so I'd change it to isRequired: true
Of course you can achieve the same result using forEach and if condition inside as other contributors mentioned, but that is not as elegant as my answer. But to honest, my method require a slightly more ticks to finish since 2 Array cycles evaluating, but remember we write code for humans, not for machines.

For now I fixed it like this, but this does not seem like the most clean/decent way. If anyone could come up with a better solution I would appreciate it.
angular.forEach(vm.schema.properties, function (prop, key) {
if (prop.isRequired == "true") {
vm.mappingFields.push({
source: null,
destination: key,
fieldType: prop.type,
isRequired: "Required",
});
};

Related

Edit readonly javascript objects

I have a read-only object that is returned by GraphQL (vue-apollo) query, the result which is read-only looks something like this:
result: {
id: 'yh383hjjf',
regulations: [{ title: 'Test', approved: false}]
})
I want to bind this to a form and be able to edit/update the values in the regulations array and save it back to the database.
at the moment when I try to edit I get the error below:
Uncaught TypeError: "title" is read-only
I tried cloning the result returned by the database using object.assign
//target template
const regulatoryApprovals = {
id: null,
regulations: [{ title: null, approved: null}]
})
regulatoryApprovals = Object.assign(regulatoryApprovals, result, {
regulations: Object.assign(regulatoryApprovals.regulations, result.regulations)
})
but this didn't work.
Does anyone know how I can properly clone the result?
regulatoryApprovals= Object.assign(regulatoryApprovals, ... indicates the problem because regulatoryApprovals is modified with Object.assign, so it would need no assignment.
Read-only regulatoryApprovals object needs to be cloned. regulations is an array and won't be merged correctly with Object.assign, unless it's known that array elements need to be replaced. It should be:
regulatoryApprovals = {
...regulatoryApprovals,
...result,
regulations: [...regulatoryApprovals.regulations, result.regulations]
}
Where { ...regulatoryApprovals, ... } is a shortcut for Object.assign({}, regulatoryApprovals, ...).

Mongoose: value not incrementing through $inc

I have been struggling with this issue for days. For some unknown reason, a specific field ("reviewCounts") is not incrementing no matter what alternative methods I try.
Here is my Schema
let itemSchema = new mongoose.Schema({
rank: Number,
image: String,
name: String,
title: String,
count: Number,
category: String,
ratings: Object,
reviewCounts: Number,
reviews: Array,
tags: Object,
})
and this is the update method:
Item.findOneAndUpdate({name: item,title:title}, {
$inc:{"reviewCounts":1},
$set:averageQuery,
$inc:query
},{strict:false},
function (err, data) {
}
}
$inc works completely find on "query" not it does not increment "reviewCounts". I have tried using $set to manually set the value, but that did not work too. I doubled-checked and confirmed that the field is int32 as intended. What could be the reason behind this issue?
When you build your update statement this way:
{
$inc:{"reviewCounts":1},
$set:averageQuery,
$inc:query
}
you're duplicating the $inc key in your JavaScript object. JavaScript interprets such code as:
{
$set:averageQuery,
$inc:query
}
so simply last usage of particular key "wins" thus you loose the reviewCounts part.
You need to make sure that there's only one $inc and you can use the spread operator to combine your $inc's:
$inc:{ ...query, "reviewCounts":1 }

Can't get state to update array inside of an object correctly in REACT/REDUX

I am going to break this down step by step for what I want to happen so hopefully people can understand what I am wanting.
Using React/Redux, Lodash
I have many post that are sent from a back end api as an array. Each post has an _id. When I call on the action getAllPost() it gives me back that array with all the post. This is working just fine.
I then dispatch type GET_ALL_POSTS and it triggers the reducer reducer_posts to change/update the state.
reducer:
export default function(state = {}, action) {
switch(action.type) {
case GET_ALL_POSTS:
const postsState = _.mapKeys(action.payload.data, '_id');
//const newPostsState = _.map(postsState, post => {
//const newComments = _.mapKeys(post.comments, '_id');
//});
return postsState;
break;
default:
return state;
break;
}
}
As you can see I change the array into one giant object that contains many post as objects with keys that are equal to their '_id'. This works just fine and returning this part of the state also works fine.
As I mentioned each of these posts has a comments value that is an array. I would like to change the comments array into one large object that holds each comment as an object with a key that is equal to their '_id' just like I did in the post.
Now I need to do this all at once and return the newly created state with One large object that contains all the post as objects and on each of those post there should be a comments object that contains all the comments as objects. I will try to write some example code to show what I am trying to do.
Example:
BigPostsObject {
1: SinglePostObject{},
2: SinglePostObject{},
3: SinglePostObject {
_id: '3',
author: 'Mike',
comments: BigCommentObject{1: SingleCommentObject{}, 2: SingleCommentObject{}}
}
}
I hope that the example kind of clears up what I am trying to do. If it still is confusing as to what I am doing then please ask and also please do not say things like use an array instead. I know I can use an array, but that is not helpful to this post as if others want to do it this way that is not helpful information.
Write a function that processes all the comments from the comments array for each post you have in the posts array:
function processComment(post) {
post.bigCommentsObject = _.mapKeys(post.comments, '_id');
// now the comments array is no longer needed
return _.omit(post, ['comments']);
}
Now use that function to turn each comments array into a big object with all the comments WHILE it still is in the array. Then afterwards turn the array itself in a big object:
const commentsProcessed = _.map(action.payload.data, procesComment);
const postsState = _.mapKeys(commentsProcessed, '_id');
I believe nowadays JS builtin function can do this without requiring external libraries. Anyway this should be the way to go. I will really encourage you getting back to js builtin functions.
var data = [
{
_id: '3',
title: 'Going on vaccation',
comments:[
{_id: 1, comment: 'hello'},
{_id: 2, comment: 'world'}
]
},
{
_id: '2',
title: 'Going to dinner',
comments:[
{_id: 1, comment: 'hello'},
{_id: 2, comment: 'world'}
]
}
]
//you can use JS builtin reduce for this
var transformedPost= _.reduce(data, function(posts, post) {
var newPost = Object.assign({}, post)
newPost._id=post._id
//you can use js builtin map for this
newPost.comments = _.mapKeys(post.comments, '_id')
// if you are using es6, replace the last three line with this
//return Object.assign({}, posts, {[newPost._id]: newPost})
var item = {}
item[newPost._id]=newPost
return Object.assign({}, posts, item)
},{});
console.log(transformedPost)
https://jsbin.com/suzifudiya/edit?js,console

map in javascropt/node or another clean approach

I have two arrays (models and modelsErrors), I want to map the reuslt and merge them in a way that if the inputs are the following :
models=['user', 'employee', 'customers'];
modelsErrors=['userError', 'employeeError', 'customersError'];
Desired output should be:
Results=[{model: user, err: userError}, {model: employee, err: employeeError}, {model: customers, err: customersError}]
I guess it is possible to use .map of javascript; if not I'm looking for a clean way like .map() function: My attempt:
models=['user', 'employee', 'customers'];
modelsErrors=['userError', 'employeeError', 'customersError'];
var results = models.map(function(model){
return {model: model, err: modelsErrors[model]}
})
console.log(results);
I'm looking for a clean way if map is not possible...
Please let me know if you need more clarification
Thanks
You were almost there; just use the index argument of the callback to find the corresponding value of the other array:
models.map(function(value, index) {
return {
model: value,
err: modelsErrors[index]
};
});

knockout validating multiple levels deep observable array

Hi I need to create a custom validator that will be aplyed for each element of an observable array using knockout validation plugin. The structure of my object will look something like this when I post it to the server:
var viewModel = {
evaluationFormDataContract: {
studentAssignmentInstanceId: value,
evaluationType: value,
categories: array[
CategoriesOnEvaluationDataContract1 = {
memo: value,
categoryId: value,
title: value,
// Fields needed for validation
hasMemo: value,
memoIsMandatory: value
questions: array[
QuestionsOnEvalCategoryDataContract1 = {
memo: value,
grade: value,
hasGrade: value,
hasMemo: value,
showOnlyMemo: value
},
QuestionsOnEvalCategoryDataContract2 = {
memo: value,
grade: value,
hasGrade: value,
hasMemo: value,
showOnlyMemo: value
}]
},
CategoriesOnEvaluationDataContract2 = {
memo: value,
categoryId: value,
title: value,
// Fields needed for validation
hasMemo: value,
memoIsMandatory: value
questions: array[
QuestionsOnEvalCategoryDataContract1 = {
memo: value,
grade: value,
hasGrade: value,
hasMemo: value,
showOnlyMemo: value
},
QuestionsOnEvalCategoryDataContract2 = {
memo: value,
grade: value,
hasGrade: value,
hasMemo: value,
showOnlyMemo: value
},
QuestionsOnEvalCategoryDataContract3 = {
memo: value,
grade: value,
hasGrade: value,
hasMemo: value,
showOnlyMemo: value
}]
}, ]
}
}
Now the validation will have to be applyed only on the two nested arrays and will be done based on some properties.
The first validation has to be done on each object of the categories array and it will check if hasMemo and memoIsMandatory if this is the case memo will be required.
The second validation will be done on each object of questions array and it will check if hasGrade if that is the case grade will be required.
The last validation will be done on hasMemo and showOnlyMemo and are will be used for the memo value on the questions object.
Reading the documentation for the validation plugin I found how I would extend a simple observable .Witch it seems to be done something like this:
ko.validation.rules['mustEqual'] = {
validator: function (val, otherVal) {
return val === otherVal;
},
message: 'The field must equal {0}'
};
But I do not think this will work for the structure of my viwmodel.How can I create validators for each observable in my observableArrays?
First off, I agree with Tomalak. Rather than posting a bunch of nonsense that your code "looks something like", you should post some actual code that is readable. For instance, I can't tell if you are using any observable, computed or observableArray members, so I just have to assume that everything is observable or observableArray and there are no computed members.
Now, you said:
The first validation has to be done on each object of the categories array and it will check if hasMemo and memoIsMandatory if this is the case memo will be required.
Let me just say that naming a property hasMemo and that mean that the memo field is required is TERRIBLE! If you call something hasMemo, it should mean that the thing in question has a memo. And why would you need to look at both hasMemo and memoIsMandatory to see if memo is required? Ditto for hasGrade.
Regardless, what you need is just to add validation to each of the observables on your classes. Wait, that's another assumption. You are using classes, right? You're not just creating a single object and giving it a bunch of nested arrays and objects without using constructors, are you? Well I'll proceed assuming that you're creating constructors and leave it at that.
I'll just focus on your first validation because the second one is just like it and the third one is unintelligible to me. So let's say your "CategoriesOnEvaluationDataContract1" object uses the following constructor:
function Category() {
var self = this;
self.categoryId = ko.observable();
self.hasMemo = ko.observable();
self.memoIsMandatory = ko.observable();
self.memo = ko.observable();
//etc etc...
}
You need to extend memo with a validator, in this case you want the required validator. That would look like this:
self.memo = ko.observable().extend({ required: true });
This makes it so that memo is always required. But that is not what you want, you want it to be required when hasMemo and memoIsMandatory are both true, right?. So this is what you need to do:
self.memo = ko.observable().extend({ required: { onlyIf: function() {
return self.hasMemo() && self.memoIsMandatory();
} } });
There. That's all there is to it. You should be able to figure out the rest. And if not, just let me know. :)

Categories