Filter array for angular2 + ngrx - arrays

I am trying to use this filter in an angular+ngrx app:
fetchById(id: number): Document {
return this.store.select( s => s.documents ).filter( obj => obj.id == id )[0]
}
documents is an array of Documents, and the latter have an id property. The idea is to filter the array to select one document. However, I get this error message:
Property id does not exist on type Document[]. I don't understand the message, since obj is an element of the array, hence a certain object which has an id property. Any ideas on what is wrong here? Thanks!

Let's break down the operations to understand what's going on...
fetchById(id: number): Observable<Document> {
const documents: Observable<Document[]> = this.store.select(s => s.documents);
const filtered: Observable<Document[]> = documents.filter((obj: Document[]) => obj.id == id );
const result: Observable<Document> = filtered.first();
return result;
}
Notice that Observable#filter()'s callback takes an array, not an individual item. This is different from Array#filter() and the cause of the error message. Also, see that I can't retrieve the first item from an observable with [0], use first() instead. Then I changed the return type to return an observable.
Yet, it doesn't work. When we think about filtering an array from an observable, probably we want to use Observable#map() to produce another array with a single element.
fetchById(id: number): Observable<Document> {
return this.store
.select(s => s.documents)
.map(list => obj.find(obj => obj.id === id));
.first();
}
If we want to return the last value of the observable, we can do this:
fetchById(id: number): Document | null {
let result: Document | null = null;
this.store
.select(s => s.documents)
.map(list => obj.find(obj => obj.id === id) || null);
.first()
.subscribe(item => result = item);
return result;
}
I typed it with | null because the document with the specified id may not exist.

Related

How to filter an array by an array in typescript and return result

I am able to print the result, how would I return the result here to be assigned to an object
Attribute text is a string that we must split
this.childArrayAll.filter(x => x.attributeText1.split(",").forEach(y => {
if(this.searchFilterForm.controls['types'].value.includes(y) )
console.log(x);
}))
You can use Array's some method to do it in clean and optimized way.
this.childArrayAll.filter(x => {
const attributes = x.attributeText1.split(",");
// Check if there is a match
return attributes.some(attribute => this.searchFilterForm.controls['types'].value.includes(attribute));
});
Your idea is good, but you overuse array functions which makes it misunderstandable. And filter requires a true/false value, but forEach does not return that.
To make your logic simpler, I'd suggest you put an explicit logic for filter and return a proper true/false value based on your condition.
this.childArrayAll.filter(x => {
const attributes = x.attributeText1.split(",")
for(const attribute of attributes) {
if(this.searchFilterForm.controls['types'].value.includes(attribute)) {
//found matched attribute
return true
}
}
//no matched attributes
return false
})

react check array values based on specific keys returning true/false if values found

I am trying to figure out how to do this but can't seem to wrap my head around it..
I have an address object
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
I want to check for any key that has a value but only specific keys
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
I want to check all keys in objProps against my address object and if any one of them contains a value return true (doesn't matter if its 1 or all 6).. If all keys don't contain a value then return false (Sometimes I will get an address object that has all null values)
I've tried various ways to accomplish this but have failed in each one.
The variation I am working on now is using reduce. While it doesn't meet my needs I thought I could check the resulting array and if length was greater than 0 than I have my answer..
Work-in-progress:
function hasAddressData(obj: any) {
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const keysWithData = objProps.reduce((accumulator, key) => {
const propExistsOnObj = obj.hasOwnProperty(key);
let keyHasData = [];
if (obj[key].length > 0 ) {
keyHasData = obj[key]
}
if (!propExistsOnObj) {
accumulator.push(key);
} else if (keyHasData) {
const equalValueKeyIndex = accumulator.indexOf(key);
accumulator.splice(equalValueKeyIndex, 1);
}
return accumulator;
});
return keysWithData;
}
The above is messed up I know and doesn't work.. Just learning this stuff.. anyone have a suggestion or comment?
Check that .some of the objProps, when looked up on the obj, contain a value. (Either with Boolean or by comparing against '')
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"",
"addressLine2":"",
"city":"",
"state":"Indiana",
"state_code":"",
"zip":"",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":""
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
function checkKeys(target, props) {
return props.some((prop) => {
return target.hasOwnProperty(prop) && target[prop];
});
}
Explanation: some iterates through the props you want to check, returning true immediately when one is found (i.e. the callback returns true). If no props are found (i.e. no callback returns true), some returns false.
hasOwnProperty ensures that you are only checking properties on target, and not looking up the prototype chain. target[prop] checks for a truthy value. You may need to modify this last check if you're going to be handling values other than strings.

FindIndex value continuously returning as a -1?

I have the following expressions:
This fetches the ID parameter passed through the URL:
editUserId = this.route.snapshot.paramMap.get('id');
Then i used FindIndex on an array of objects to find the index of an element in the array equal to the above mentioned ID value fetched from the URL:
this.userToUpdate = this.allUsers.findIndex((x: any) => x.UserId === this.editUserId);
(this.allUsers) being my array of objects.
Although the findIndex continuously returns the value -1 ..
I have tried the following two methods, both of which return the same error: "This condition will always return 'false' since the types 'number' and 'string | null' have no overlap":
this.userToUpdate = this.allUsers.findIndex((x: any) => parseInt(x.UserId) === this.editUserId);
this.userToUpdate = this.allUsers.findIndex((x: any) => Number(x.UserId) === this.editUserId);
Any reason as to why this may be happening?
Seventh, I guess that can be the editUserId needs to be number, you can try it:
this.userToUpdate = this.allUsers.findIndex((x: any) => Number(x.UserId) === Number(this.editUserId));
or
editUserId = this.route.snapshot.paramMap.get('id');
editUserId = Number(editUserId)
I guess it because when you return the params to your variable, always it will be string

How do I filter out empty objects in Typescript?

I hope you can help me.
I am creating a map using mapbox-gl and I am rendering some coordinates [lon, lat].
Some values are null though, so I am trying to filter them out using a reducer, but for those empty values I am only creating an empty object I can't delete and getting an error (filteredPositions.lon -> error: Property 'lon' does not exist on type '{}')
How can I only render those clusters with not null values? Here is my code:
<Cluster ClusterMarkerFactory={clusterMarker} zoomOnClick>
{systems.map(({ last_position, id }) => {
const filteredPositions = Object.entries(last_position).reduce((a,[k,v]) => (v == null ? a : {...a, [k]:v}), {})
return (
<SystemMarker
key={id}
coordinates={[filteredPositions.lon, filteredPositions.lat]}
heading={0}
id={id}
onClick={onSystemSelect}
/>
)
})}
</Cluster>
A reducer is supposed to do some computations on the elements of a list and return only one element. The typical use case is to compute the sum of the elements of a list:
let list = [1,2,3,4,5]
let sum = list.reduce((prev, curr) => prev + curr, 0); // 15
If you want to filter the list. there is a filter method. try to use that.
const filteredPositions = Object.entries(last_position).filter(([k,v]) => v !== null)

React not re-rendering when state changed from hook

Edit: Thanks for the help everyone. I needed to change the reference of the array and fixed it by doing:
setData([...sorted])
I am currently rendering out a list of tasks. This is a snippet of my return function within a functional component:
const [ data, setData ] = useState( mockData )
<tbody>
{ data.map(d => <TaskItem key={d.claimable} task={d}/>) }
</tbody>
When I click on a certain button on the page, the dataset gets sorted and I call setData(sortedData)
For some reason, the table isnt being re-rendered with the sorted data. Is there something I did wrong here?
This is the sort function:
function filterByContactAmount():void {
let sorted = data.sort((a:any, b:any) => {
let aTimesContacted:number = a.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
let bTimesContacted:number = b.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
if ( aTimesContacted > bTimesContacted ) {
return 1
}
if ( bTimesContacted > aTimesContacted ) {
return -1
}
return 0;
})
console.log(sorted)
setData(sorted)
}
Its because you are using the same ref of the array, you need set the new data with
setData(old => "sorted data");
to change the reference of the state and it updates
function filterByContactAmount():void {
let sorted = data.sort((a:any, b:any) => {
let aTimesContacted:number = a.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
let bTimesContacted:number = b.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
if ( aTimesContacted > bTimesContacted ) {
return 1
}
if ( bTimesContacted > aTimesContacted ) {
return -1
}
return 0;
})
console.log(sorted)
setData(old => [...sorted]) // Sorted is the new state sorted
}
You are mutating sate, the other answer is probably not the best because you are still mutating state and then setting state with a copy of the already mutated value.
The sort function can also be optimized. Maybe try the following:
function filterByContactAmount() {
let sorted = data
.map(d => ({//map shallow copies the array
...d,//shallow copies the item
sortedNum: d.data.person.contact.list.reduce(//do this once for every item, not for every time sort callback is called
(acc, val) => acc + val.history.length,
0
),
}))
.sort((a, b) => a.sortedNum - b.sortedNum);
console.log(sorted);
setData(sorted);
}
I think issue is located under d.claimable, I suppose it is boolean variable type. You must know that every key prop must be unique. Check if you have for example.id property, if not add it.
Uniqueness of key prop is very important during reconciliation process.
Unique identifier with a group of children to help React figure out which items have changed, have been added or removed from the list. It’s related to the “lists and keys” functionality of React described here.
Very nice article about reconciliation.
<tbody>
{ data.map(d => <TaskItem key={d.claimable} task={d}/>) }
</tbody>

Resources