Comparing arrays from Mongoose using ShouldJS - javascript

Taking an array such as ['hello', 'there'] and storing that in a Mongoose document with a schema such as
tags: { type: Array }
using something such as:
Something.create({ tags: ['hello', 'there']}, cb);
Then using ShouldJS to check that the document matches my supplied array I would expect this:
doc.tags.should.eql(['hello', 'there']);
But it does not. If I console.log doc tags I get:
[hello, there]
Notice that the quotes are gone. The doc.tags is indeed an array (I can check instanceof Array) and I can also use shouldjs with
doc.tags.should.have.keys('hello');
doc.tags.should.have.keys('there');
Anyone have an idea as to why my array doesn't match anymore?

Your array is not a real json Array: it's a MongooseArray, with additional methods.
To make should.eql work with mongoose array, first use toObject():
doc.tags.toObject().should.eql(['hello', 'there']);

Related

Why does using slice in MongoDB updateOne does not delete an item from an array but only replaces it with a weird object?

so there the array coursesFollowing
and inside it objects like this for example:
objInside = {
username:"SweetWhite"
courseTitle:"TOMATOaaaaa"
progress:0
}
and I have a function that finds the right object index in the array of objects of that kind of object
and it seems to work in terms of index found and object, however my update request works weirdly when I execute it:
User.updateOne({username: req.body.username}, {coursesFollowing:{$slice: [removeIndex , 1]}}, function(err,result){
if(err){return next(err)}
})
it find the right user to put it in, and also the right field, however it does not remove the object in that removeIndex index, but deletes all the items in the array and puts only one object in, with the array named $slice with the removeIndex as first value and the second value is 1, judging from the data it has not deleted all the other objects but simply replaced them with itself, the operation $slice pushed itself into the array and did not execute I think? I am mildly confused.
anyways, how do I fix this? how do I delete an index of the array without pushing the weird $slice object array thingi?
Thanks!
If you notice your updated document you might realize that you are not updating anything in the array actually using your $slice operator. Instead you are setting a new value for the array. And that is why you see the first element as $slice something and the second as 1.
$slice operator is used with $push and is used to limit the number of elements in the array field finally. The doc doesn't mention removing an array element with this, given its index.
According to this answer there is no simple way to do this.
There is $pull, but it does not work with the given index. It works based on a query condition for the object in the array. So if you are first figuring out the element index and then using it in the update query. You can do that directly.
If you want to use a JS function you can use splice, which does in-place updates. Use the $function
User.updateOne({username: req.body.username}, [{ $set:
{ "coursesFollowing": { $function: { body: function(coursesFollowing) { coursesFollowing.splice(removeIndex, 1); return coursesFollowing; }, args: ["$coursesFollowing"], lang: "js" }} }
}], function(err,result){
if(err){return next(err)}
})

Validate if object has string

I have this array
switched = {"Restaurant":1,"Hotel":2,"Beauty shop":3,"Physiotherapist":4,"Dentist":5,"Bar":6,"Coffee shop":7}
and this object
result = [{"Google Place URL":"http://www.restaurant.com","Business Type":"Restaurant"},{"Google Place URL":"http://www.hotel.com","Business Type":"Hotel"}]
I want to validate if every item of the result contains words of switched
also, I'm already working with a for that returns each item separately
item[csvBusinessType] = Restaurant
how can I validate if item[csvBusinessType] is included in switched?
I have tried with
let n = switched.includes(item[csvBusinessType]);
but I get Uncaught TypeError: switched.includes is not a function
There is not good native method for this. Better to use lodash library https://www.npmjs.com/package/lodash
Here is an example
const _ = require('lodash');
console.log( _.includes(switched, item[csvBusinessType]));
I converted the object to string and used includes, I don't know if it will be the best way, but I think it is the most supported by browsers
let businessListArray = JSON.stringify(switched);
let checkBusiness = businessListArray.includes(item[csvBusinessType]);
I want to validate if every item of the result contains words of switched
If you want to validate that every item of the result object array contains words of the switched array, you should look into JS Array methods and working with Objects, just as #Andreas suggested.
In this specific case, your switched object has keys that appear to match the csvBusinessType of your item objects. This is helpful as we can use Object.keys() to grab all the keys of your switched object as an array of strings.
const businessTypes = Object.keys(switched) // = ["Restaurant","Hotel","Beauty shop","Physiotherapist","Dentist","Bar","Coffee shop"]
Now that we have grabbed the keys that you want to check against, we can use the JS Array method every() to perform a test against every object in your result object array. In this case, we will be testing that the obect's Business Type is included in our newly created businessTypes array.
const resultsValid = result.every((resultObject) => businessTypes.includes(resultObject["Business Type"]));
resultsValid is a boolean that is true if all objects in result had a Business type that matched one of the keys of your switched object.
NOTE: You may want to check Letter Casing before some of these comparisons unless you want to explicitly match only exact matches ("Beauty shop" will NOT match "Beauty Shop" for example).

Using method sort() for sorting symbols in element of array

I need to sort an element cinema of array arr by symbols unicode (in the output it must been like "aceinm"). I know that in this case we need to use method sort(). But I do know how inject sort method for array element.
Please, help. Code below are not working.
Error: arr[1].sort is not a function.
var arr = ["cinema"];
arr[1].sort();
console.log(arr[1]);
If you want to sort your string, you can easily do this by splitting and joining the string.
"cinema".split ("").sort ().join ("")
// aceimn
Or, in your case:
arr[0] = arr [0].split ("").sort ().join ("")
// arr: ["aceimn"]
If you need to sort all strings in an array, use map ().
arr = arr.map (itm => itm.split ("").sort ().join (""))
You are referring arr[1] which is not available also you have to split in order to sort the letters.
var arr = ["cinema"];
var sorted = arr[0].split('').sort();
console.log(sorted, sorted.join(''));
This should do what you want:
var arr = ["cinema"];
console.log(arr[0].split("").sort().join(""));
EDIT: I see the same solution has been proposed by several others. I'll expand a bit on it.
Since you want to sort by the letters in the word cinema, and cinema is at index 0, you get the string "cinema" by calling arr[0], and then split the string with the method .split(""). This turns the string into an array that you can .sort() in the way you attempted initially.
The error you got, "Error: arr[1].sort is not a function", tells you that .sort() is not a function of the string element. Once you've turned the string into an array (for example with .split()), the .sort() function becomes available.

Javascript includes method does not work with MongoDB IDs

I was trying to use includes method to check existing MongoDB Id in an array but it is always returning false.
Is it a some kind of bug in the includes method or am I doing something wrong below is my code
let user = req.user._id;
let keyword= req.query.q;
let userSearchHistory = await UserSearchHistory.findOne({searchKeywords: keyword}).exec();
if (!userSearchHistory.users.includes(user))
{
}
where users in my database is an array with ObjectId refering to user collection
I also tried converting user object
let user= mongoose.Types.ObjectId(req.user._id);
But still the same result.
Converting ObjectIds to String working for me but then I have to remove the references in my model what is the proper way to handle this ?
This is probably happening because userSearchHistory.users returns an array of objectIds.
You can try replacing userSearchHistory.users.includes(user) with userSearchHistory.users.map(user=>user.toString()).includes(user).
This way you will be able to convert the array of objectIds to array of strings before applying 'includes' function.
Edit : Did you try trimming spaces in 'req.user._id' by using req.user._id.trim() and then casting it to objectId?

Find if already exists, JS and Lodash

So I have an interesting issue I am not sure how to follow, I need to use lodash to search two arrays in an object, looking to see if x already exists, lets look at a console out put:
There are two keys I am interested in: questChains and singleQuests, I want to write two seperate functions using lodash to say: find me id x in the array of objects where questChains questChainID is equal to x.
The second function would say: Find me a quest in the array of objects where singleQuests questTitle equals y
So if we give an example, you can see that questChainId is a 1 so if I pass in a 1 to said function I would get true back, I don't actually care about the object its self, else I would get false.
The same goes for singleQuests, If I pass in hello (case insensitive) I would get back true because there is a quest with the questTitle of 'Hello'. Again I don't care about the object coming back.
The way I would write this is something like:
_.find(theArray, function(questObject){
_.find(questObject.questChains, function(questChain){
if (questChain.questChainId === 1) {
return true;
}
});
});
This is just for the quest chain id comparison. This seems super messy, why? Because I am nesting lodash find, I am also nesting if. It gets a bit difficult to read.
Is this the only way to do this? or is there a better way?
Yeah it can be expressed more simply.
Try something like this:
var exampleArray = [{
questChains: [{
questChainId: 1,
name: 'foo'
}, {
questChainId: 2,
name: 'bar'
}],
singleQuests: [{
questTitle: 'hello world'
}]
}, {
questChains: [{
questChainId: 77,
name: 'kappa'
}]
}];
var result = _.chain(exampleArray)
.pluck('questChains')
.flatten()
.findWhere({ questChainId: 2 })
.value();
console.log('result', result);
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
Using chain and value is optional. They just let you chain together multiple lodash methods more succinctly.
pluck grabs a property from each object in an array and returns a new array of those properties.
flatten takes a nested array structure and flattens it into flat array structure.
findWhere will return the first element which matches the property name/value provided.
Combining all of these results in us fetching all questChain arrays from exampleArray, flattening them into a single array which can be more easily iterated upon, and then performing a search for the desired value.
Case-insensitive matching will be slightly more challenging. You'd either need to either replace findWhere with a method which accepts a matching function (i.e. find) or sanitize your input ahead of time. Either way you're going to need to call toLower, toUpper, or some variant on your names to standardize your search.

Categories

Resources