Angular2 - search in array inside of an array - arrays

I have an array of events and I have a search() function (below) that filters through it. Currently it only filters for name or date. Within my payload I have an array of tags, and I'm trying to figure out how to include it in the filtering of this. My current work-around is buttons that re-run the /event endpoint with search params which I don't like.
search(array: any[], query: string, excludeProps?: string|string[], dateFormat?: string) {
if (!query || !this._objArrayCheck(array)) {
return array;
}
const lQuery = query.toLowerCase();
const isoDateRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; // ISO UTC
const dateF = dateFormat ? dateFormat : 'medium';
const filteredArray = array.filter(item => {
for (const key in item) {
if (item.hasOwnProperty(key)) {
if (!excludeProps || excludeProps.indexOf(key) === -1) {
const thisVal = item[key];
if (
// Value is a string and NOT a UTC date
typeof thisVal === 'string' &&
!thisVal.match(isoDateRegex) &&
thisVal.toLowerCase().indexOf(lQuery) !== -1
) {
return true;
} else if (
// Value is a Date object or UTC string
(thisVal instanceof Date || thisVal.toString().match(isoDateRegex)) &&
// https://angular.io/api/common/DatePipe
// Matching date format string passed in as param (or default to 'medium')
this.datePipe.transform(thisVal, dateF).toLowerCase().indexOf(lQuery) !== -1
) {
return true;
}
}
}
}
});
return filteredArray;
}
I tried adding an addition else after the date finder and leverage a for() loop on item.tags but it seems typescript doesn't really let you do a traditional for loop.
What is the actual method of handling this within Ang/Typescript?
Update
search(array: any[], query: string, excludeProps?: string|string[], dateFormat?: string) {
if (!query || !this._objArrayCheck(array)) {
return array;
}
const lQuery = query.toLowerCase();
const isoDateRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; // ISO UTC
const dateF = dateFormat ? dateFormat : 'medium';
const filteredArray = array.filter(item => {
for (const key in item) {
if (item.hasOwnProperty(key)) {
if (!excludeProps || excludeProps.indexOf(key) === -1) {
const thisVal = item[key];
if (
// Value is a string and NOT a UTC date
typeof thisVal === 'string' &&
!thisVal.match(isoDateRegex) &&
thisVal.toLowerCase().indexOf(lQuery) !== -1
) {
return true;
} else if (
// Value is a Date object or UTC string
(thisVal instanceof Date || thisVal.toString().match(isoDateRegex)) &&
// https://angular.io/api/common/DatePipe
// Matching date format string passed in as param (or default to 'medium')
this.datePipe.transform(thisVal, dateF).toLowerCase().indexOf(lQuery) !== -1
) {
return true;
} else if (
typeof thisVal === 'string'
) {
for (var i=0; i < item.tags.length; i++) {
if (thisVal === item.tags[i]){
return true;
}
}
}
}
}
}
});
return filteredArray;
}
I was thinking something similar to this but it doesn't seem to be returning anything.
sample Payload
// collection: events
{
"_id" : ObjectId("59dda4b702ac8332bcb90921"),
"title" : "Gantt Chart Example222dsda",
"location" : "Home",
"startDatetime" : ISODate("2018-01-01T12:30:00.000-05:00"),
"endDatetime" : ISODate("2018-01-02T12:30:00.000-05:00"),
"description" : "dsad",
"viewPublic" : true,
"tags" : [
"Gaming",
"Social",
"home",
"misc"
],
"__v" : NumberInt("0")
}
My function should be able to match 'gantt' to the title and still show the row. It should also be able to match 'gaming', 'social' 'home' etc and pull this same record due to the tags. It should be able to pull from the start or end date time.

Related

How to get array after convert by reduce()

How to get an array after converted by reduce()?
Before convert:
After convert:
newProductsDetail = newProductsDetail.reduce((acc, curr) => {
const { categoryName } = curr
if (!acc[categoryName]) {
acc[categoryName] = []
}
acc[categoryName].push(curr)
return acc
}, [])
I would like get newProductsDetail[categoryName]
Have any way to approach this?
Thanks to all of you!
you can filter it like this
productDetail.filter( detail => { if(detail !== undefined && detail[0] !== undefined) return detail[0].categoryName === myCategory; return false; } )
Set initial value as object and not list as follows if you want to get hold of value from newProductsDetail[categoryName]:
newProductsDetail = newProductsDetail.reduce((acc, curr) => {
const { categoryName } = curr
if (!acc[categoryName]) {
acc[categoryName] = []
}
acc[categoryName].push(curr)
return acc
}, {}) // dictionary/object is initial and not list
If you only want array and some manipulation, try to use map to iterate and convert final output to array of something.

Filter data based on 2 values

I am creating an application with a requirement for 2 search bars. One to search by name and another to search by tag. The search should also work when the user puts both name and tag in the searchbars.
The data from the api is in the format
{
"students" : [
{
"city": "city",
"company" : "company",
"email" : "email",
"firstName" : "firstName",
"grades" : ["12", "54"],
"id" : "1",
"lastName" : "lastName",
"pic" : "url",
"skill" : "skill"
},
],
}
My application is running on https://temp-application.netlify.app/
Currently the application does search with name as it is supposed to be.
But as soon as I attach search by tag functionality with it. Everything breaks down.
This is how I implemented searching by both name and tag.
useEffect(() => {
let filteredResults = results.filter((result) => {
const name = result.firstName + " " + result.lastName;
const isName = name
.toLowerCase()
.includes(searchName.toLowerCase());
const tags =
result.tag !== undefined ? result.tag.toString() : false;
const isTag =
typeof tags === "string"
? tags.toLowerCase().includes(searchTag.toLowerCase())
: false;
return isName && isTag;
});
setStudents(filteredResults);
}, [searchName, searchTag]);
Notice that 'tag' array is not present in the object returned by the API
The link you gave is not working.
For Tags, you are searching in the "tag" string in the Student(result) object but there is no key named tag in the given data.
Even if you have the tag key, you are probably not getting the result because you are doing a "&&" instead of "||"
I solved it. The problem was that I was trying to do the search in one-go. But instead I needed to make a if-else chain to achieve the result.
Here is the final function
let filteredResults = results.filter((result) => {
const name = result.firstName + " " + result.lastName;
const tags =
result.tag !== undefined ? result.tag.toString() : false;
if (searchName !== "" && searchTag === "") {
return name.toLowerCase().includes(searchName.toLowerCase());
} else if (searchName === "" && searchTag !== "") {
return typeof tags === "string"
? tags.toLowerCase().includes(searchTag.toLowerCase())
: false;
} else if (searchName !== "" && searchTag !== "") {
const isName = name
.toLowerCase()
.includes(searchName.toLowerCase());
const isTag =
typeof tags === "string"
? tags.toLowerCase().includes(searchTag.toLowerCase())
: false;
return isName && isTag;
} else if (searchName === "" && searchTag === "") {
return true;
}
return false;
});
setStudents(filteredResults);
}, [searchName, searchTag]);

How to parse /Date(-62135596800000)/ to null

I get from the server dates, sometimes null, and my filter parse /Date(-62135596800000)/ to 0001-01-01 but I would like to have empty string.
How can I do this?
Here's my filter:
filter('myDateFormat', function() {
return function (data) {
return moment(data).format("YYYY-MM-DD");
};
})
Well your server is returning you null or a date string.
null can be handled by simple null check but for your other input "Date(-62135596800000)" which is a valid date, you can't return empty string, without explicitly checking for it.
moment(Date(-62135596800000)).isValid() // return 'true'
you may try below code.
// if inputDate not defined or null
if (!inputDate || inputDate == null) {
return '';
} else if (moment(inputDate).isValid()){
return moment(inputDate).format("DD MM YYYY");
} else {
return '';
}
but if your server is returning this specific date occasionally 'Date(-62135596800000)' and you want to avoid this, you can put explicit check for it.
if (!inputDate || inputDate == null) {
return '';
}
if (inputDate == 'Date(-62135596800000)') {
return '';
} else if (moment(inputDate).isValid()) {
return moment(inputDate).format("DD MM YYYY");
} else {
return '';
}
The filter could be improved like this:
filter('myDateFormat', function() {
return function (data) {
return data > 0 ? moment(data).format("YYYY-MM-DD") : "";
};
})
First of all decide what should be your minimum date.
For example SQL's minimum date is 1753-01-01
After that compare your date with this minimum date like:
filter('myDateFormat', function() {
return function (data) {
var minDate = moment('1753-01-01');// decide your minimum date
return moment(data).diff(minDate) > 0 ? moment(data).format("YYYY-MM-DD") : "";
};
})

Ag-Grid Filter Comma

im using ag-Grid, but there is a issue when it filters my data, when i filter my data in the price column, it only works with numbers dot and not with commas.
Link: https://plnkr.co/edit/LDdrRbANSalvb4Iwh5mp?p=preview
Practical Example:
In the Price column select box equal and above insert "1.5" and than try inserting "1,5"
This is because this filter is a native one.
If you want to handle custom behaviour, define your own filter.
Documentation : https://www.ag-grid.com/angular-grid-filtering/index.php
A quick and dirty solution would be to monkey patch the NumberFilter like this :
NumberFilter.prototype.doesFilterPass = function (node) {
if (this.filterNumber === null) {
return true;
}
var value = this.valueGetter(node);
if (!value && value !== 0) {
return false;
}
var valueAsNumber;
if (typeof value === 'number') {
valueAsNumber = value;
}
else {
valueAsNumber = parseFloat(value.replace(',','.'));
}
switch (this.filterType) {
case EQUALS:
return valueAsNumber === this.filterNumber;
case LESS_THAN:
return valueAsNumber < this.filterNumber;
case GREATER_THAN:
return valueAsNumber > this.filterNumber;
default:
// should never happen
console.warn('invalid filter type ' + this.filterType);
return false;
}
};
Then changed line is here :
valueAsNumber = parseFloat(value.replace(',','.'));
So i found the problem, first i had to convert the value has a string than i needed to replace the dot by the comma, the problem with the answer above was first because of the data type and than the order of the properties of the replace function, but the problem now is that is not filtering correctly, if i search using equal option if gives me 2 values, instead a fixed one, code looks something like this:
Code:
NumberFilter.prototype.doesFilterPass = function (node) {
if (this.filterNumber === null) {
return true;
}
var value = this.valueGetter(node);
if (!value && value !== 0) {
return false;
}
var valueAsNumber;
if (typeof value === 'number') {
value = value.toString()
valueAsNumber = parseFloat(value.replace('.',','));
}
else {
valueAsNumber = parseFloat(value.replace('.',','));
}
switch (this.filterType) {
case EQUALS:
return valueAsNumber === this.filterNumber;
case LESS_THAN:
return valueAsNumber < this.filterNumber;
case GREATER_THAN:
return valueAsNumber > this.filterNumber;
default:
// should never happen
console.warn('invalid filter type ' + this.filterType);
return false;
}
};
Possible Solution:
NumberFilter.prototype.onFilterChanged = function () {
var filterText = utils_1.default.makeNull(this.eFilterTextField.value);
if (filterText && filterText.trim() === '') {
filterText = null;
}
var newFilter;
if (filterText !== null && filterText !== undefined) {
console.log(filterText);
// replace comma by dot
newFilter = parseFloat(filterText.replace(/,/g, '.'));
console.log(newFilter);
}
else {
newFilter = null;
}
if (this.filterNumber !== newFilter) {
this.filterNumber = newFilter;
this.filterChanged();
}
};

How can I control the format of a Ext.form.NumberField value when a form submits?

I've extended Ext.form.Numberfield to show thousands separators and always show two decimal places:
Ext.override(Ext.form.NumberField, {
baseChars: "0123456789,",
setValue: function(v){
v = typeof v == 'number' ? v : String(v).replace(this.decimalSeparator, ".").replace(/,/g, "");
//v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
v = isNaN(v) ? '' : Ext.util.Format.number(this.fixPrecision(String(v)), "0,000,000.00");
this.setRawValue(v);
return Ext.form.NumberField.superclass.setValue.call(this, v);
},
fixPrecision: function(value){
var nan = isNaN(value);
if (!this.allowDecimals || this.decimalPrecision == -1 || nan || !value) {
return nan ? '' : value;
}
return parseFloat(value).toFixed(this.decimalPrecision);
},
validateValue: function(value){
if (!Ext.form.NumberField.superclass.validateValue.call(this, value)) {
return false;
}
if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
return true;
}
value = String(value).replace(this.decimalSeparator, ".").replace(/,/g, "");
if (isNaN(value)) {
this.markInvalid(String.format(this.nanText, value));
return false;
}
var num = this.parseValue(value);
if (num < this.minValue) {
this.markInvalid(String.format(this.minText, this.minValue));
return false;
}
if (num > this.maxValue) {
this.markInvalid(String.format(this.maxText, this.maxValue));
return false;
}
return true;
},
parseValue: function(value){
value = parseFloat(String(value).replace(this.decimalSeparator, ".").replace(/,/g, ""));
return isNaN(value) ? '' : value;
}
});
The problem is that on form submit, the value sent in the POST includes the commas, forcing me to parse as a string on the server side. Is there a way to send the raw number value instead of this special comma-formatted value?
Instead of sending these parameters:
referenceSales 10,000,000.00
salesGoal 11,000,000.00
I want to send these:
referenceSales 10000000.00
salesGoal 11000000.00
Of course you realize NumberField extends TextField, so there is no raw value. (wysiwyg) I suggest using a regular expression on submission.

Resources