Typescript- filter: Provide multiple conditions in filter dyanmically - typescript2.0

I am bit curious if there is any way where i can have multiple conditions dynamically in the filter expression. For example
dataSource.filter(data => data.x === a && data.x === b && data.x === c)
it can have 'n' number of conditions, all will be dynamic only.
Thanks

Not like that, no. You would have to pass in a function that accepts data or data.x and check against every possible condition.
function filterData(possibleValuesOfX: number[] = []) {
return (data: Data) => {
for (let value of possibleValuesOfX) {
if (data.x === value) {
return true;
}
}
return false;
}
}
const filteredDataSource = dataSource.filter(filterData([a, b, c]));

Related

ag grid vue grouping set columns expanded after component reload

I use ag-grid table - I am grouping the columns e.g. like:
Is it possible to set columns expanded the same way they were after components reload?
How to save how columns were expanded and then reload it?
One way is to store the ids of the nodes which are expanded (I do so in local storage as there aren't many rows in my table and I know I won't store anything confidential). Then on reload, retrieve the nodes that should be expanded and expand them:
<ag-grid-angular
(rowGroupOpened)="onRowGroupOpened()"
(gridReady)="onGridReady($event)">
</ag-grid-angular>
localStorageKey = 'storage-key-name';
onRowGroupOpened(): void {
let allExpanded = true;
const expandedNodeDetails: string[] = [];
if (this.myGrid.gridApi != null) {
this.myGrid.gridApi.forEachNode(node => {
if (node.group || (node.allChildrenCount > 0)) {
if (!this.restoringExpandedNodes) {
expandedNodeDetails.push(node.key);
}
}
});
}
if (!this.restoringExpandedNodes) {
localStorage.setItem(this.localStorageKey, JSON.stringify(expandedNodeDetails));
}
}
onGridReady(): void {
this.restoreExpandedNodes();
}
restoreExpandedNodes(): void {
const itemsInStorage = JSON.parse(localStorage.getItem(this.localStorageKey));
if ((itemsInStorage != null) && (this.myGrid != null) && (this.myGrid.gridApi != null)) {
this.restoringExpandedNodes = true;
this.myGrid.gridApi.forEachNode(node => {
if (node.group || (node.allChildrenCount > 0)) {
const expandedDetails = this.getExpandedDetails(node, null);
const index = itemsInStorage.findIndex(storageItem => storageItem === expandedDetails);
if (index !== -1) {
node.expanded = true;
} else if ((itemToSelect != null) && (node.key == itemToSelect.ItemFullName)) {
node.expanded = true;
}
}
});
this.myGrid.gridApi.onGroupExpandedOrCollapsed();
this.restoringExpandedNodes = false;
}
}
I've had to sanitise this code so please let me know if something doesn't make sense

Vue.js - Filter multiple fields

I'm trying to filter a on a nested array inside an array of objects in an Vue.js. Here's a snippet of the component code:
filteredProducts: function() {
if (!this.filters.appointments.length && !this.filters.powers.length && !this.filters.materials.length && !this.filters.lamps.length) return this.products;
return this.products.filter(product => {
return product.filter(p => {
let filteredAppointments = this.filters.appointments ? _.difference(this.filters.appointments, p.appointment.split(',')).length === 0 : true,
filteredPowers = this.filters.powers ? this.filters.powers.includes(p.total_power_lamps) : true,
filteredMaterials = this.filters.materials ? this.filters.materials.includes(p.material) : true,
filteredLamps = this.filters.lamps ? this.filters.lamps.includes(p.count_lamps) : true,
filteredPrice = p.price >= this.rangePrice[0] && p.price <= this.rangePrice[1];
return filteredAppointments && filteredPowers && filteredMaterials && filteredLamps && filteredPrice;
}).length > 0;
});
}
How do I display only the filters that are used ? For example, if only one filter is selected, then no other filters are needed. Also, if several are selected, it should filter by both.
I think this is what you're looking for
filteredProducts () {
let results = this.products
if(this.filters.appointments.length) {
results = results.filter(logicFilterHere)
}
if(this.filters.powers.length) {
results = results.filter(logicFilterHere)
}
if(this.filters.materials.length) {
results = results.filter(logicFilterHere)
}
return results
}

Is there a way to wait until a function is finished in React Native?

I'm trying to get information (true/false) from AsyncStorage in a function and create a string which is importent to fetch data in the next step. My problem is, the function is not finished until the string is required.
I tried many solutions from the internet like async function and await getItem or .done() or .then(), but none worked out for me.
//_getFetchData()
AsyncStorage.getAllKeys().then((result) => { //get all stored Keys
valuelength = result.length;
if (valuelength !== 0) {
for (let i = 0; i < valuelength; i++) {
if (result[i].includes("not") == false) { //get Keys without not
AsyncStorage.getItem(result[i]).then((resultvalue) => {
if (resultvalue === 'true') {
if (this.state.firstValue) {
this.state.channels = this.state.channels + "channel_id" + result[i];
console.log("channel: " + this.state.channels);
}
else {
this.state.channels = this.state.channels + "channel" + result[i];
}
}
});
}
return this.state.channels;
_fetchData() {
var channel = this._getFetchData();
console.log("channel required: " + channel);
}
The current behaviour is that the console displays first "channel required: " than "channel: channel_id0".
Aspects in your question are unclear:
You don't say when this.state.firstValue is set, and how that relates to what you are trying to accomplish.
You have a for-loop where you could be setting the same value multiple times.
You mutate the state rather than set it. This is not good, see this SO question for more on that.
There are somethings we can do to make your code easier to understand. Below I will show a possible refactor. Explaining what I am doing at each step. I am using async/await because it can lead to much tidier and easier to read code, rather than using promises where you can get lost in callbacks.
Get all the keys from AsyncStorage
Make sure that there is a value for all the keys.
Filter the keys so that we only include the ones that do not contain the string 'not'.
Use a Promise.all, this part is important as it basically gets all the values for each of the keys that we just found and puts them into an array called items
Each object in the items array has a key and a value property.
We then filter the items so that only the ones with a item.value === 'true' remain.
We then filter the items so that only the ones with a item.value !== 'true' remain. (this may be optional it is really dependent on what you want to do)
What do we return? You need to add that part.
Here is the refactor:
_getFetchData = async () => {
let allKeys = await AsyncStorage.getAllKeys(); // 1
if (allKeys.length) { // 2
let filteredKeys = allKeys.filter(key => !key.includes('not')); // 3
let items = await Promise.all(filteredKeys.map(async key => { // 4
let value = await AsyncStorage.getItem(key);
return { key, value }; // 5
}))
let filteredTrueItems = items.filter(item => items.value === 'true'); // 6
let filteredFalseItems = items.filter(item => items.value !== 'true'); // 7
// now you have two arrays one with the items that have the true values
// and one with the items that have the false values
// at this points you can decide what to return as it is not
// that clear from your question
// return the value that your want // 8
} else {
// return your default value if there are no keys // 8
}
}
You would call this function as follows:
_fetchData = async () => {
let channel = await this._getFetchData();
console.log("channel required: " + channel);
}
Although the above will work, it will not currently return a value as you haven't made it clear which value you wish to return. I would suggest you build upon the code that I have written here and update it so that it returns the values that you want.
Further reading
For further reading I would suggest these awesome articles by Michael Chan that discuss state
https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0
https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
https://medium.learnreact.com/setstate-takes-a-function-56eb940f84b6
I would also suggest taking some time to read up about async/await and promises
https://medium.com/#bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8
And finally this article and SO question on Promise.all are quite good
https://www.taniarascia.com/promise-all-with-async-await/
Using async/await with a forEach loop
Try this instead. Async functions and Promises can be tricky to get right and can be difficult to debug but you're on the right track.
async _getFetchData() {
let channels = "";
let results = await AsyncStorage.getAllKeys();
results.forEach((result) => {
if (result.includes("not") === false) {
let item = await AsyncStorage.getItem(result);
if (item === 'true') {
console.log(`channel: ${result}`)
channels = `channel_id ${result}`;
}
}
});
return channels;
}
_fetchData() {
this._getFetchData().then((channels) => {
console.log(`channel required: ${channel}`);
});
}
what if you wrap the _getFetchData() in a Promise? This would enable you to use
var channel = this._getFetchData().then(console.log("channel required: " + channel));
Otherwise the console.log won't wait for the execution of the _getFetchData().
This is what the console.log is telling you. it just logs the string. the variable is added after the async operation is done.
UPDATE
I would try this:
//_getFetchData()
AsyncStorage.getAllKeys().then((result) => { //get all stored Keys
valuelength = result.length;
if (valuelength !== 0) {
for (let i = 0; i < valuelength; i++) {
if (result[i].includes("not") == false) { //get Keys without not
AsyncStorage.getItem(result[i]).then((resultvalue) => {
if (resultvalue === 'true') {
if (this.state.firstValue) {
this.state.channels = this.state.channels + "channel_id" + result[i];
console.log("channel: " + this.state.channels);
}
else {
this.state.channels = this.state.channels + "channel" + result[i];
}
}
});
}
return new Promise((resolve, reject) => {
this.state.channels !=== undefined ? resolve(this.state.channels) : reject(Error('error '));
}
_fetchData() {
var channel = this._getFetchData().then(console.log("channel required: " + channel));
}
maybe you must change the this.state.channels !=== undefined to an expression that's matches the default value of this.state.channels.

Filtering by date range within a computed array?

Within my app I'm trying to develop the ability to filter my returned array of offers if they fall within a set of dates set using a datepicker.
My datepicker emits the values to two properties within a range object - this is filters.range.startDate & filters.range.endDate. Each offer in my array has the properties, offer.dates.start & offer.dates.end.
I've added the below statement in my computed property which doesn't break the computed, just returns no results regardless of dates.
Does anyone have any advice?
EDIT- Added the entire computed property with the date range statement as the last condition.
computed: {
filteredOffers() {
let filtered = this.offers.filter(offer => {
return (offer.island === this.filters.islandFilter || this.filters.islandFilter === 'All') // Island
&& (offer.starrating === this.filters.starRating || this.filters.starRating === 'All') // Star Rating
&& (offer.board === this.filters.boardBasis || this.filters.boardBasis === 'All') // Board Basis
&& (offer.duration === this.filters.duration || this.filters.duration === 'All') // Duration
&& (offer.price.from < this.filters.price) // Price
&& (this.filters.travelby === 'sea' && offer.travel.air === false || this.filters.travelby === 'All') // Sea or Air
&& (this.filters.range.startDate >= offer.dates.start && offer.dates.end <= this.filters.range.endDate) // DATE RANGE!!
});
if (this.sortby === 'ascending') {
return filtered.sort((a, b) => {
return a.price.from - b.price.from;
})
} else {
return filtered.sort((a, b) => {
return b.price.from - a.price.from;
})
}
}
}
First, I would transform your date objects to timestamp in milliseconds, just avoid some format errors when you compare.
let date = new Date();
let timestamp = date.getTime();
After that, I guess your logic is not correct, because your end date on filter should be greater than your offer end date, and your start date on filter should be smaller than your offer start date.
this.filters.range.startDate <= offer.dates.start && this.filters.range.endDate >= offer.dates.end

How to compare two routes by hand

How can I manually (programmatically) compare two routes and find out if they are same? (if router-link-active or router-link-exact-active would be present)
Generally i need this sort of a function
/*
#params route1, route2 : Route
*/
function isActivated(route1, route2) {
/* comparing them somehow */
return {
exactActive,
active
};
}
Use Case:
I have a NestedLink.vue component which is wrapper over router-link.
It takes to prop just as router-link (and passes it down to child router-link). If current route is active, nested links will apear nearby.
My approach:
function isActivated(route1, route2) {
if (
route1.matched.some(record =>
record.regex.test(route2.fullPath)
)
) {
return { exactActive: true };
}
return { exactActive: false };
}
It may tell when routes are exact-active but not for not-exact-active.
This is the code, used inside router-link component.
const START = '/';
const trailingSlashRE = /\/?$/;
function isObjectEqual (a, b) {
if ( a === void 0 ) a = {};
if ( b === void 0 ) b = {};
// handle null value #1566
if (!a || !b) { return a === b }
var aKeys = Object.keys(a);
var bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) {
return false
}
return aKeys.every(function (key) {
var aVal = a[key];
var bVal = b[key];
// check nested equality
if (typeof aVal === 'object' && typeof bVal === 'object') {
return isObjectEqual(aVal, bVal)
}
return String(aVal) === String(bVal)
})
}
function isSameRoute (a, b) {
if (b === START) {
return a === b
} else if (!b) {
return false
} else if (a.path && b.path) {
return (
a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
a.hash === b.hash &&
isObjectEqual(a.query, b.query)
)
} else if (a.name && b.name) {
return (
a.name === b.name &&
a.hash === b.hash &&
isObjectEqual(a.query, b.query) &&
isObjectEqual(a.params, b.params)
)
} else {
return false
}
}
So, here's how to use it inside component:
// OR JUST A STRING '/home'
let link = { name: 'home' }
// will return true of false
let result = isSameRoute(this.$router.resolve(link).resolved, this.$route)