I am mapping a subset of user data to an object of a refined data set. Inside the map i want to check if a variable is null or undefined, and if yes, then to set this variable to a placeholder value.
The issue I am facing is that declaring an if statement inside the map is causing an error, but even though a map can have an index as a parameter, how can we use it functionally with a conditional Statement? Any insight most appreciated.
return this.UserService.search(url)
.map((data) => {
console.log(data);
data.result = <any> data.result.map((user, index) => ({
// if statement causing error here
if(user.name === null || user.name === undefined){
// error with this if condition
},
id: user.id,
name: user.name,
type: user.type,
password: user.password,
}));
return data;
}).map(data => ({
meta: { totalItems: data.size },
data: data.result,
}));
You're attempting to use an object literal as the return type, but naturally, an if statement (or any statement) can't be inside object literal syntax.
So instead, define a function body, which also uses curly braces, and put your code inside with an explicit return statement.
// Starts function body instead of single expression-v
data.result = <any> data.result.map((user, index) => {
if (some_condition) {
return "some value"; // The object?
} else {
return "other value"; // A different object?
}
/*
// I assume these are to be used in the object you return
id: user.id,
name: user.name,
type: user.type,
password: user.password,
*/
});
You can express conditions in literal maps, but it is somewhat ugly.
return {
a: 1,
...(some_condition && {
b: 1,
})
};
As far as i know you can't do that with JUST a map.
however you could follow it up with a filter() function:
const newArray = oldArray.map((value, index) => condition ? value : null).filter(v => v);
you basicaly iterate over each item and then return the value or null depending on your condition.
Now once you have the map you just filter it by removing the null values from the array.
Notice that the original array is not altered and a new one is returned.
thanks for the idea #user8897421 for the idea. i just wanted to turn it into a one liner.
Related
I have my object structured as below and I want to find the product with provided ID.
0 :{
id: 0,
title: 'xxxx',
url: "www.test.com"
quantity: 100
},
1 :{
id: 10,
title: 'xxxx',
url: "www.test.com"
quantity: 100
},
// and so on...
In order to search nested attribute within the object, I have written the below function:
export const selectProductById = (state, productId) => {
const obj_index = Object.keys(state.products).find(function(idx) {
if (state.products[idx].id == productId) {
return idx;
}
}
return state.products[obj_index]
}
This works but I will always get a warning during compilation of my react app.
Expected '===' and instead saw '=='
But if I change this into === the code will not work anymore, does anyone knows how to change this so that it follows JSLint rules ?
It sounds like the productId is not a number. Cast it to a number first:
if (state.products[idx].id === Number(productId)) {
But you should return a truthy or falsey value from the .find callback, not something that you're iterating over (since you may not be sure whether it's truthy or falsey, and it's potentially confusing). Return the result of the === comparison instead:
const { products } = state;
const obj_index = Object.keys(products).find(
key => products[key].id === Number(productId)
);
I am making a server using nodejs & express in which user can request some data and server send response. But, the data is array and I want to send a json response to the user. So, I used forEach() method of array and use Object.assign(), so that I can get object. But the problem is I cannot use 'index' argument of forEach() method while 'value' argument is properly getting used inside the callback function. When I use only 'index' argument, then my code runs ok but I want to use both arguments at the same time.
route.get('/getGPX/:number', passport.authenticate('jwt', { session: false }), (req, res) => {
gpx.find({ username: req.user.username }, (err, result) => {
if (err) console.log(err);
if (result) {
if (result.length === 0) {
res.end();
console.log('ended')
}
else {
var jsonRes = {};
result.forEach((value, index) => {
I can use 'value' but not 'index' from the arguments
jsonRes = Object.assign({ index: value.data }, jsonRes);
})
res.json({data: jsonRes});
}
}
})
I even tried using global var, but even it's not working, how can I use index as well as value argument at the same time
What is the JSON structure that you want ?
if you want a json like :
{
0: "my first value",
1: "second"
}
You just miss [] around index, here you put 'index' as a key not the value of index. So you override the index key in each iteration.
Here is the code that use the value of index as a key in a json.
jsonRes = Object.assign({[index]: value.data}, jsonRes)
See here for a working example with more examples : https://repl.it/#Benoit_Vasseur/nodejs-playground
Object.assign mutates the left-most parameter. it does not produce a new object. Since you are passing a literal object every time the jsonRes is going to be the last result.
Put jsonRes in the left instead - Object.assign(jsonRes, {index: value.data})
You might want to use a simple reduce instead of forEach and Object.assign:
} else {
var jsonRes = result.reduce((r, v, i) => {r[i] = v.data; return r}, {});
res.json({data: jsonRes});
}
Ho can I avoid to write all fields with = ""?
const defaultPlayer = {
name: "",
surname: "",
age: "",
skill: ""
}
// ...
mapPropsToValues = ({ player }) => player || defaultPlayer
Is there in javascript that I can use to avoid write all the time = ""?
I mean, if I already know that defalut value of every field is "" (empty string) how can I do instead of write every field explicitly?
You can accomplish this using a with block and a Proxy object and eval:
let defaultPlayer;
with (new Proxy({}, {
has(o, key) {
try { eval(key); }
catch (e) { return true; }
},
get() {
return '';
}
})) {
defaultPlayer = {
userName,
age,
surname,
isAdmin: false
};
}
console.log(defaultPlayer);
When you omit a property value, it looks for a variable with the same name as the key. For example, {x} is the same as {x: x}. We can use the with() statement to have JavaScript check for properties of an object before looking for variables. Instead of a normal object, we'll define a Proxy that returns '' for any variables that it doesn't see in the local environment (by testing for an exception when evaling their name).
☠️ This is an obscene hack with nasty edge cases. Never use it. ☠️
I have an object, which looks like this:
{
'somename1':{
status:'success',
data:{
key:'value1',
field2:'',
field3:'',
field4:'',
field5:'',
recordSts:'done',
}
},
'someOtherName':{
status:'success',
data:{
key:'value2',
field2:0,
field3:'',
recordSts:'progress',
field5:'',
field6:0,
}
}
}
In this object, I have two fields key and recordSts, which are not null if the status is success.
I want to filter this object using lodash and the output should look like this
{
'somename1':{
status:'success',
data:{
key:'value1',
status:'value1',
}
},
'someOtherName':{
status:'success',
data:{
key:'value1',
status:'value1',
}
}
}
Simply I want to delete the keys which having null or empty or 0 values.
I tried this:
_.map(records, 'key'); //records contains my input object
But it gives only the value of one field, instead, I want the name of the field and the second field also. Please help.
Thanks.
You can use _.pickBy with our custom predicate, like
_.forEach(records, function(record) {
record.data = _.pickBy(record.data, function(val) {
return !!val;
}
})
you can use _.omitBy to remove falsy values
let res = _.forOwn(records, o => {
o.data = _.omitBy(o.data, v => {
return !v;
});
return o;
});
You can use mapValues(), assign(), and pickBy():
_.mapValues(
records,
r => _.assign({}, r, { data: _.pickBy(r.data) })
);
This produces a new records object, without mutating the original. What I like about pickBy() is that by default, it'll only return properties with truthy values. Notice how we're not passing a predicate? This works because the default predicate is identity(), which simply uses the value as a truth test.
When a user registers with my API they are returned a user object. Before returning the object I remove the hashed password and salt properties. I have to use
user.salt = undefined;
user.pass = undefined;
Because when I try
delete user.salt;
delete user.pass;
the object properties still exist and are returned.
Why is that?
To use delete you would need to convert the model document into a plain JavaScript object by calling toObject so that you can freely manipulate it:
user = user.toObject();
delete user.salt;
delete user.pass;
Non-configurable properties cannot be re-configured or deleted.
You should use strict mode so you get in-your-face errors instead of silent failures:
(function() {
"use strict";
var o = {};
Object.defineProperty(o, "key", {
value: "value",
configurable: false,
writable: true,
enumerable: true
});
delete o.key;
})()
// TypeError: Cannot delete property 'key' of #<Object>
Another solution aside from calling toObject is to access the _doc directly from the mongoose object and use ES6 spread operator to remove unwanted properties as such:
user = { ...user._doc, salt: undefined, pass: undefined }
Rather than converting to a JavaScript object with toObject(), it might be more ideal to instead choose which properties you want to exclude via the Query.prototype.select() function.
For example, if your User schema looked something like this:
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
name: {
type: String,
required: true
},
pass: {
type: String,
required: true
},
salt: {
type: String,
required: true
}
});
module.exports = {
User: mongoose.model("user", userSchema)
};
Then if you wanted to exclude the pass and salt properties in a response containing an array of all users, you could do so by specifically choosing which properties to ignore by prepending a minus sign before the property name:
users.get("/", async (req, res) => {
try {
const result = await User
.find({})
.select("-pass -salt");
return res
.status(200)
.send(result);
}
catch (error) {
console.error(error);
}
});
Alternatively, if you have more properties to exclude than include, you can specifically choose which properties to add instead of which properties to remove:
const result = await User
.find({})
.select("email name");
The delete operation could be used on javascript objects only. Mongoose models are not javascript objects. So convert it into a javascript object and delete the property.
The code should look like this:
const modelJsObject = model.toObject();
delete modlelJsObject.property;
But that causes problems while saving the object. So what I did was just to set the property value to undefined.
model.property = undefined;
Old question, but I'm throwing my 2-cents into the fray....
You question has already been answered correctly by others, this is just a demo of how I worked around it.
I used Object.entries() + Array.reduce() to solve it. Here's my take:
// define dis-allowed keys and values
const disAllowedKeys = ['_id','__v','password'];
const disAllowedValues = [null, undefined, ''];
// our object, maybe a Mongoose model, or some API response
const someObject = {
_id: 132456789,
password: '$1$O3JMY.Tw$AdLnLjQ/5jXF9.MTp3gHv/',
name: 'John Edward',
age: 29,
favoriteFood: null
};
// use reduce to create a new object with everything EXCEPT our dis-allowed keys and values!
const withOnlyGoodValues = Object.entries(someObject).reduce((ourNewObject, pair) => {
const key = pair[0];
const value = pair[1];
if (
disAllowedKeys.includes(key) === false &&
disAllowedValues.includes(value) === false
){
ourNewObject[key] = value;
}
return ourNewObject;
}, {});
// what we get back...
// {
// name: 'John Edward',
// age: 29
// }
// do something with the new object!
server.sendToClient(withOnlyGoodValues);
This can be cleaned up more once you understand how it works, especially with some fancy ES6 syntax. I intentionally tried to make it extra-readable, for the sake of the demo.
Read docs on how Object.entries() works: MDN - Object.entries()
Read docs on how Array.reduce() works: MDN - Array.reduce()
I use this little function just before i return the user object.
Of course i have to remember to add the new key i wish to remove but it works well for me
const protect = (o) => {
const removes = ['__v', '_id', 'salt', 'password', 'hash'];
m = o.toObject();
removes.forEach(element => {
try{
delete m[element]
}
catch(O_o){}
});
return m
}
and i use it as I said, just before i return the user.
return res.json({ success: true, user: await protect(user) });
Alternativly, it could be more dynamic when used this way:
const protect = (o, removes) => {
m = o.toObject();
removes.forEach(element => {
try{
delete m[element]
}
catch(O_o){}
});
return m
}
return res.json({ success: true, user: await protect(user, ['salt','hash']) });