Find matching elements in array and do something for each match - lodash

I'm a little overwhelmed by all the functions available to me in Lodash, so I hope someone can point me to the one I'm sure exists which will do the following for me. I want to be able to pass an array and a search condition, and have it loop through all the matched items, allowing me to run a function for each. What I have at the moment is something akin to this:
_.each(myArray, (item) => {
if (item.field=="whatever") {
console.log("Matched by "+item.name);
}
});
This works fine, of course. It's just that I'm sure Lodash has a way for me to move the item.field=="whatever" into the function arguments somehow, and I'd prefer to go with the more idiomatic Lodash way if I can.

It's just that I'm sure Lodash has a way for me to move the item.field == "whatever" into the function arguments somehow
If you want to find all the matching items in an array based on the arguments you pass in, then you could use the _.filter method, which can use the _.matches shorthand internally:
_.filter(myArray, { field: 'whatever' });
However, you would still need to loop over the items if you want to do something for each match:
_.each(_.filter(myArray, { field: 'whatever' }), item => {
console.log("Matched by " + item.name);
});
Alternatively, if you want a different way of writing this, you can wrap the filtered items with the lodash object wrapper, _(), which essentially enables chaining, thereby allowing you to chain the _.each() method:
_(_.filter(myArray, { field: 'whatever' })).each(item => {
console.log("Matched by " + item.name);
});
Or a more readable version:
var matchedItems = _.filter(myArray, { field: 'whatever' });
_(matchedItems).each(item => {
console.log("Matched by " + item.name);
});
Personally, I would probably just keep what you originally wrote since it's short, readable and easy to maintain.

Related

How to chain a custom function to express-validator?

I am using express-validator and I have chained some validations to link parameter like this:
route.post('/landing-pages/:link/blocks',
[
param('link').trim().escape().isString()
],
controller.addBlocks);
I need to add some chained functions like trim and escape to be able to modify the value.
I can use custom method like the following to add new validation:
route.post('/landing-pages/:link/blocks',
[
param('link').trim().escape().isString().custom((value, { req, location, path }) =>
{
//return true/false based on custom validation.
}
],
controller.addBlocks);
But instead of validating and returning true/false, I want to modify value and change it's original source exactly the way that trim or escape is doing it. for example, I want to replace some characters or I want to remove some words, etc.
Is there anyway to do it with express-validator?
you can chain customSanitizer function for that purpose
param('link').trim().escape().isString().customSanitizer(value => {
// imagine we have a sanitizer function
const sanitizedLink = linkSanitizer(value)
return sanitizedLink;
})

How could I do not repeat the selection process in Cypress?

How could I do not repeat the selection process in Cypress?
E.g. if I have:
cy
.get("...").find("...")...eq(0)...should("...")
.get("...").find("...")...eq(1)...should("...");
How could I avoid duplicating the .get("...").find("...")... part if at some point I need to pick either the eq(0) or the eq(1)?
You can use .as() to alias an element.
// Just aliasing the base
cy.get('foo').find('bar').as('something');
cy.get('#something').eq(0).should('exist');
cy.get('#something').eq(1).should('exist');
// aliasing the specific elements
cy.get('foo').find('bar').eq(0).as('firstEl');
cy.get('#firstEl').should('exist');
cy.get('foo').find('bar').eq(1).as('secondEl');
cy.get('#secondEl').should('exist');
You could also use a custom command.
// If the selectors in `get` and `find` are constant, you could do a custom command
Cypress.Commands.add('myGet', (index) => {
return cy.get('foo').find('bar').eq(index);
})
cy.myGet(0).should('exist');
// Or if you wanted to be able to customize the get and find
Cypress.Commands.add('myGet', (get, find, index) => {
return cy.get(get).find(find).eq(index);
})
cy.myGet('foo', 'bar', 0).should('exist');
You can create a custom command for this. Go to cypress/support/commands.js and write:
Cypress.Commands.add('selectElement', (index) => {
cy.get('selector').find('selector').eq(index).should('be.visible')
})
And then in your test just write:
cy.selectElement(1)
cy.selectElement(2)

Vue: Setting Data by matching route query

I'm attempting to set data fields provided by an array based on the Vue Router query. For example, when someone lands on my website using example.com/?location=texas, I want to set the location data by an array.
An example the array:
locations {
{
slug: "texas",
tagline: "Welcome to Texas",
}, {
slug: "california",
tagline: "Welcome to California",
}
}
I know this should be done using a computed property, however I am unable to get anything functioning. I've tried simple tests like if (this.slug.location === "texas"), and I cannot get the location data to populate. I would also like to provide default data in case there are no route matches.
Any help is extremely appreciated!
Edit:
I can accomplish this in a very manual way. Right now, I'm setting the query in data by the following:
slug: this.$route.query.location
I can display specific text by doing something like:
h3(v-if="slug === 'texas'") This will show for texas
h3(v-else-if="slug === 'california'") This will show for California
h3(v-else) This is default
The issue with this approach is there are various elements I need to customize depending on the slug. Is there any way I can create an array, and move whichever array matches a key in an array to the data??
You should be able to access a query param using the following (link to Vue Router documentation):
this.$route.query.location
So based on what you listed I would do something like...
export default {
computed: {
displayBasedOnLocationQueryParam() {
switch(this.$route.query.location) {
case 'texas':
return 'Welcome to Texas'
default:
return 'hello there, generic person'
}
}
}
}
Note that I'm not using your array explicitly there. The switch statement can be the sole source of that logic, if need be.

Cypress Get Attribute value and store in Variable

I want to get the Attribute value and store in a variable how we can achieve this in cypress
In my case I want to get the complete class value and store it in variable.
This code just give me the attribute class value but how I can store the fetch value in variable
cy.get('div[class*="ui-growl-item-container ui-state-highlight ui-corner-all ui-shadow ui-growl-message"]').invoke('attr', 'class')
I was trying to compare the style of one element with another to make sure they were equal. Here's the code that seems to work for me.
cy.get('.searchable-group-selector-card-image')
.eq(4)
.invoke('attr', 'style')
.then(($style1) => {
const style1 = $style1
})
A good way to solve this kind of scenario is to use the alias mechanism. One could leverage this functionality to enqueue multiple elements and then check all of them together by chaining the results. I've recently come to a case in an SPA where the assertion had to happen between elements that were spread across different angular routes (call them different pages).
In your use case, this would like:
cy.get('.searchable-group-selector-card-image')
.eq(4)
.invoke('attr', 'style')
.as('style_1')
cy.get('.another-element')
.invoke('attr', 'style')
.as('style_2')
// later on for example you could do
cy.get('#style_1').then(style_1 => {
cy.get('#style_2').then(style_2 => {
// Both values are available and any kind of assertion can be performed
expect(style_1).to.include(style_2)
});
});
This is described in Variables and Aliases section of the Cypress Documentation.
Here is how I got the value of for attribute in a label tag which had text "Eat" inside.
cy.contains('Eat').then(($label) => {
const id = $label.attr('for');
}
Most important thing is to get the selector right, so it exactly finds the value you are looking for. In this case you already found it. By using then() gives you the ability to store it in a variable.
cy.get('div[class*="ui-growl-item-container ui-state-highlight ui-corner-all ui-shadow ui-growl-message"]').invoke('attr', 'class')
.then($growl-message => {
const message = $growl-message.text()
//do the checks with the variable message. For example:
cy.contains(message)
})
Note that the scope of the variable is within the curly brackets. Thus using the variable has to be within those curly brackets.

Is there a way to bind a variable number of queries?

I'm coding an app for managing shift work. The idea is pretty simple: the team is shared between groups. In those groups are specific shifts. I want to get something like that:
Group 1
- shift11
- shift12
- shift13
Group 2
- shift21
- shift22
- shift23
I already made a couple of tests, but nothing is really working as I would like it to: everything reactive, and dynamic.
I'm using vue.js, firestore (and vuefire between them).
I created a collection "shiftGroup" with documents (with auto IDs) having fields "name" and "order" (to rearrange the display order) and another collection "shift" with documents (still auto IDs) having fields "name", "order" (again to rearrange the display order, inside the group) and "group" (the ID of the corresponding shiftGroup.)
I had also tried with firestore.References of shifts in groups, that's when I was the closest to my goal, but then I was stuck when trying to sort shifts inside groups.
Anyway, with vuefire, I can easily bind shiftGroup like this:
{
data () {
return {
shiftGroup: [], // to initialize
}
},
firestore () {
return {
shiftGroup: db.collection('shiftGroup').orderBy('order'),
}
},
}
Then display the groups like this:
<ul>
<li v-for="(group, idx) in shiftGroup" :key="idx">{{group.name}}</li>
</ul>
So now time to add the shifts...
I thought I could get a reactive array of shifts for each of the groups, like that:
{
db.collection('shift').where('group', '==', group.id).orderBy('order').onSnapshot((querySnapshot) => {
this.shiftCollections[group.id] = [];
querySnapshot.forEach((doc) => {
this.shiftCollections[group.id].push(doc.data());
});
});
}
then I'd call the proper list like this:
<ul>
<li v-for="(group, idx) in shiftGroup" :key="idx">
{{group.name}}
<ul>
<li v-for="(shift, idx2) in shiftCollections[group.id]" :key="idx1+idx2">{{shift.name}}</li>
</ul>
</li>
</ul>
This is very bad code, and actually, the more I think about it, the more I think that it's just impossible to achieve.
Of course I thought of using programmatic binding like explained in the official doc:
this.$bind('documents', documents.where('creator', '==', this.id)).then(
But the first argument has to be a string whereas I need to work with dynamic data.
If anyone could suggest me a way to obtain what I described.
Thank you all very much
So I realize this is an old question, but it was in important use case for an app I am working on as well. That is, I would like to have an object with an arbitrary number of keys, each of which is bound to a Firestore document.
The solution I came up with is based off looking at the walkGet code in shared.ts. Basically, you use . notation when calling $bind. Each dot will reference a nested property. For example, binding to docs.123 will bind to docs['123']. So something along the lines of the following should work
export default {
name: "component",
data: function () {
return {
docs: {},
indices: [],
}
},
watch: {
indices: function (value) {
value.forEach(idx => this.$bind(`docs.${idx}`, db.doc(idx)))
}
}
}
In this example, the docs object has keys bound to Firestore documents and the reactivity works.
One issue that I'm trying to work through is whether you can also watch indices to get updates if any of the documents changes. Right now, I've observed that changes to the Firestore documents won't trigger a call to any watchers of indices. I presume this is related to Vue's reactivity, but I'm not sure.