How filter works in Mongoose? - javascript

I have seen a tutorial that execute filter in mongoose schema
I want to know how this really work.
const isConversationExist = user.conversations.filter(conversation => (
conversation.userOneId === request.payload.friendId || conversation.userTwoId === request.payload.friendId)
,).length > 0)

Just break it up into pieces starting with
user.conversations.filter(A)
The filter() method creates a new array with all elements that pass the test implemented by the provided function (A).
In the code, the provided function (A) is written using ES6 arrow notation
conversation => (
conversation.userOneId === request.payload.friendId || conversation.userTwoId === request.payload.friendId
)
which can be rewritten (but not always) as
function (conversation) {
return conversation.userOneId === request.payload.friendId || conversation.userTwoId === request.payload.friendId;
}
It basically says, for each element i.e. conversation, test if either of the conversation's user IDs is equal to request.payload.friendId.
If the test passes (as in function returns true), then the element will be added to the new array; otherwise, the element will be ignored.
user.conversations.filter(A).length
This would be the size of the new array with all conversations that pass the test.
user.conversations.filter(A).length > 0
This would be a boolean whether the new array has conversations that pass the test or not i.e. isConversationExist.

Related

How to get the underscore function _.first to work on the arguments objects?

As part of the precourse for a coding bootcamp, we have to create a simpler version of the underscore JS library. I am struggling with creating the _.first function, which:
Returns an array with the first n elements of an array.
If n is not provided it returns an array with just the first element.
This is what I've got so far:
_.first = function(array, n) {
if (!Array.isArray(array)) return [];
if (typeof n != "number" || n <= 0) return [].slice.call(array, 0, 1);
return n >= array.length ? array : [].slice.call(array, 0, n);
};
It passes all test except one: "It must work on an arguments object"
I know the arguments object passes an array with all the arguments passed and it has a length property but Im struggling to work with it.
Any help would be much appreciated.
The arguments object is just that, a variable defined implicitly on each function scope that acts like an array. Has a length property and you can access the elements by using number properties like a normal array:
var _ = {};
_.first = function() {
if (arguments.length == 0) { // If there's no arguments
return [];
} else { // When there's 1 or more arguments
var array = arguments[0];
var n = arguments.length > 1 ? arguments[1] : 1; // If there's only the "array" argument ("n" is not provided), set "n" to 1
// And now your code, which has nice checks just in case the values are invalid
if (!Array.isArray(array)) {
return [];
}
if (typeof n != "number" || n <= 0) {
n = 1;
}
return [].slice.call(array, 0, n); // Don't worry if slice is bigger than the array length. It will just work, and also always return a copy of the array instead of the array itself.
}
};
console.log( _.first() );
console.log( _.first([0,1,2]) );
console.log( _.first([0,1,2], 2) );
console.log( _.first([0,1,2], 10) );
I would add something to the first answer. The arguments object is something that is normally created implicitly by JavaScript and made available inside the function body. In order to write a unit test for "It must work on an arguments object", they must explicitly define an arguments object and pass it in. This is a bad unit test because it is testing the internal working of your function. You should be free to write the function any way you like, and a unit test should test the external behaviour of the function (return value and/or side effects, based on the arguments passed).
So imo your original solution is good and the test is designed to force you to use a certain syntax for the sake of learning, but this is misleading.

Filter an array of objects using the filter method with multiple tests

I'd like to filter an array of objects based on multiple tests. For this example, I want to filter an array of objects if the values for the keys aren't null, and that one value for one key is less than 90. I'm currently doing this with a for loop like so:
let filtered = []
for (let i = 0; i < articles.length; i++) {
if (articles[i].title !== null && articles[i].title.length <= 90 &&
articles[i].date !== null &&
articles[i].image !== null &&
articles[i].description !== null &&
articles[i].link !== null) {
filtered.push(articles[i])
}
}
But it's pretty clumpy and I know the filter method can achieve something similar. But I'm unsure if it can check multiple keys and their values with the same test whilst checking if a specific value passes an independent test too.
Try:
articles.filter(article =>
Object.values(article).every(x => (x !== null))
&& article.title.length <= 90
)
Let's break this down:
articles.filter(article => ...)
.filter is a function property of type that accepts a callback argument, which it calls for each item. Essentially, we're passing it a function - not executing it right away, which it can call at its leisure. It's sort of like:
let a = alert;
We're not calling the alert function, we're just saving it to a variable. In the case of .filter, we're using it as a pseudo-variable - an argument. Internally, all .filter is doing is:
Array.prototype.filter(callbackFunc) {
newArr = [];
for (i=0;i<this.length;i++){
if (callbackFunc(this[i]) === false){ // We're calling `callbackFunc` manually, for each item in the loop.
newArr.push(this[i]);
}
}
return newArr;
}
The next bit to explain is the actual callback function we're using. It's defined with ES6 arrow syntax, but it's the equivalent of:
articles.filter(function(article){
return Object.values(article).every(x => (x !== null))
&& article.title.length <= 90
})
The first line of the callback function, Object.values(article).every(x => (x !== null)), can be broken down to:
let values = Object.values(article); // Get the value of all of the keys in the object
function checkFunction(item){ // Define a helper function
return (x !== null); // Which returns if an item is _not_ null.
}
let result = values.every(checkFunction); // Run `checkFunction` on every item in the array (see below), and ensure it matches _all_ of them.
Finally, we just need to clarify what every does. It's another example of functional JS, where functions accept callback functions as parameters. The internal code looks like this:
Array.prototype.every(callbackFunc) {
for (i=0;i<this.length;i++){
if (callbackFunc(this[i]) === false){ // We're calling `callbackFunc` manually, for each item in the loop.
return false;
}
}
// In JS, returning a value automatically stops execution of the function.
// So this line of code is only reached if `return false` is never met.
return true;
}
And && article.title.length <= 90 should hopefully be self-explanatory: while .every returns a boolean value (true or false), a true will only be returned by the callback function to the filter if the second condition is also met, i.e if the every returns true and article.title.length <= 90
The filter method does exactly this: it takes a conditional (just like that in your if statement and adds it to the array if the condition is met. Your code almost matches the filter syntax exactly, actually:
let filtered = articles.filter(article =>
article.title !== null
article.title.length <= 90 &&
article.date !== null &&
article.image !== null &&
article.description !== null &&
article.link !== null);
yes filter can do this, it just takes a function and applies it to each item in the array
array.filter(x => x.title != null && ... etc)
the examples in this section is pretty much what you are doing https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Filtering_invalid_entries_from_JSON

Why does `arr.find()` return `undefined`?

In the below code his._notifications is an array of objects, each object contains the properties shown below.
What I am trying to do is to check if each object not passed to the function func() was pushed into the array or not?
So I am using .find() and I expect it returns true if the object was passed to the function or if it already exists in the array, and false otherwise.
But the below log statement prints undefined! Why isExists is undefined? And what is the recommended way to check if an item is duplicate or not in an array?
code:
func(noti)
const isExists = this._notifications.find((obj) =>
obj.title === noti.safeTitle
&& obj.text === noti.safeText
&& obj.bannerTitle === noti.safeBannerTitle
&& obj.bannerText === noti.safeBannerText
&& obj.icon === noti.icon
&& obj.onClickCallback === noti.onClickCallback
&& obj.eNotificationType === noti.notificationType
&& obj.deleteable === noti.deletable
&& obj.withBanner === noti.withBanner
);
logger.info('[isNotificationExists] For the current notification: ', JSON.stringify(notification), ' isExists: ', isExists);
From MDN
The find() method returns the value of the first element in the array
that satisfies the provided testing function. Otherwise undefined is
returned.
The includes() method determines whether an array includes a certain
element, returning true or false as appropriate. It uses the
sameValueZero algorithm to determine whether the given element is
found.
To check if an item is duplicated in array
function hasDuplicates(array) {
return (new Set(array)).size !== array.length;
}
find() is not a suitable data structure here as it's return type is the element found not the flag(that whether it was found of not). You can use underscorejs _.contains functions for this.
http://underscorejs.org/#contains

Flat Array vs nested Array in React are treated differently

I have noticed that I can pass to React a set of nested arrays and it will render the items properly, but when I go this way, it will not complain of missing keys on my elements.
const stuff = 'a,b,c';
// Nested Array
// React is fine with it and automatically assigns keys
// Sample data: [[a, <br />], [b, <br />], [c, <br />]]
const Foo = () => <div>{stuff.split(',').map(itm => [itm, <br />])}</div>;
// Flat Array
// React warns me that I should assign a key to each element in array
// Sample data: [a, <br />, b, <br />, c, <br />]
const Bar = () => <div>{stuff.split(',').map(itm => [itm, <br />]).reduce((a, b) => a.concat(b), [])}</div>;
Sample pen:
https://codepen.io/FezVrasta/pen/NppLPR
Why does it happens? I can't find any reference to "nested arrays" support in React, neither on "auto assigned keys".
You'll notice that even though a warning is printed to the console, React still shows both Foo and Bar in your HTML being generated. React uses unique keys for reconciliations whilst trying to boost rendering performance. You can read more about this on the React reconciliation recursing on children page. Not providing keys means React cannot be as performant as it has been designed to be.
With regards to your question as to why a warning is not output to the console for nested arrays, we have to dive into the source code:
The function which generates the warning is called validateExplicitKey, and lives in the ReactElementValidator.js module.
This function is used in the validateChildKeys in the same module - looking into the source code gives the following, as of React 15.4.2:
function validateChildKeys(node, parentType) {
if (typeof node !== 'object') {
return;
}
if (Array.isArray(node)) { // 1.
for (var i = 0; i < node.length; i++) {
var child = node[i]; // 2.
if (ReactElement.isValidElement(child)) { // 3.
validateExplicitKey(child, parentType);
}
}
} else if (ReactElement.isValidElement(node)) {
// This element was passed in a valid location.
if (node._store) {
node._store.validated = true;
}
} else if (node) {
var iteratorFn = getIteratorFn(node);
// Entry iterators provide implicit keys.
if (iteratorFn) {
if (iteratorFn !== node.entries) {
var iterator = iteratorFn.call(node);
var step;
while (!(step = iterator.next()).done) {
if (ReactElement.isValidElement(step.value)) {
validateExplicitKey(step.value, parentType);
}
}
}
}
}
}
An array of arrays will enter the first code block
the child will be set to child = ["b", Object] (where 'Object' is react's virtual dom representation for the br node we created via JSX)
the array will be run through the function ReactElement.isValidElement:
ReactElement.isValidElement = function (object) {
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
};
with REACT_ELEMENT_TYPE being set as:
var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7;
The array is an object, and is not null, but it's $$typeof property hasn't been set here, so the check fails.
$$typeof hasn't been set because React only adds this property to elements it creates to identify whether something is a React Element or not. This includes native HTML elements, and not data types.
Hence the ReactElement.isValidElement check fails, and the warning is never shown.
I've been wondering the same thing recently!
From what I've understood in ReactJS's official docs about how the keys work, I would expect to get the same warning with the nested array of data, just like the one with the flat array of data, since in both cases there are no key attributes set.
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.
I actually filled in a bug report (issue) in the ReactJS official GitHub repo, describing the same case you pointed out, but simplified (without the fancy .map() and .reduce() thingies).
It looks like a bug to me.
PS: I will update my answer as soon as the React team responds to me.

Javascript function return value undefined

I am using a function to go through an array of objects and filter out the one that matches certain criteria.
var thread = underscore.find(threads, function (th) {
function result(threadArray){
threadArray.forEach(function(threads){
if (threads.owner.id === ownerId && threads.name.toLowerCase() == threadName) {
console.log(threads)
return threads
}
});
};
console.log(result(th));
return th.owner.id === ownerId && th.name.toLowerCase() === threadName;
});
'th' is the array of objects. Stepping through it, I can see that the array of objects is being iterated over using the forEach function, and that my if logic is successfully filtering out just one object because I can see it in my console "console.log(threads)", but when I try to console the return value of the function by invoking it, "console.log(result(th))", it comes back as undefined, and I can't figure out why.

Categories

Resources