pantonis
pantonis

Reputation: 6467

Array filter does not return expected result

I am trying to filter an array in typescript I get the expected result but the original collection is altered.

private filter(){
            this.data = this.originalData.slice().filter((client: Client) => {
                // Filter list of orders first
                let filteredListOfOrders = client.ListOfOrders.slice().filter((ordersItem: any) => {
                    const searchStr = JSON.stringify(ordersItem).toLowerCase();
                    return searchStr.indexOf(this.filterValue.toLowerCase()) !== -1;
                });

                // Assign filtered list of orders to existing client item 
                client.ListOfOrders = filteredListOfOrders;

                const searchStr = JSON.stringify(client).toLowerCase();
                return searchStr.indexOf(this.filterValue.toLowerCase()) !== -1;
            });
}

export class Client {
   id: number;
   name: string;
   address: string;
   orders: Array<Order>;
}

export class Order {
    id: number;
    dateOfOrder: Date;
    address: string;
}

When I type the address I want to also search the order items apart from the clients and return only clients with the filtered address and only the orders with the filtered address. The above works partially but it causes me a problem as this.originalData is modified and only the orders that match the filter are set. this.originalData should remain intact.

Does anyone knows why? Thought that slice returns a shallow copy

Upvotes: 0

Views: 99

Answers (1)

jcalz
jcalz

Reputation: 328272

Hmm, this doesn't fit in a comment so I'm expanding it to an answer:

You're not using the term "shallow" in a way that makes sense. Values aren't shallow or deep. Copies are shallow or deep.

A shallow copy of an object (an array is just an object) is a new object whose properties hold references to the very same objects as the original object's properties. If you modify the contents of one of the properties of the shallow copy, you will see that modification in the analogous property of the original object, since both properties are pointing to the very same object.

If you want to be able to modify the contents of a copy without touching the original contents, you need to perform a deep copy, where you recurse down through all the properties of each object and all their properties, and make actual copies.

A full deep copy is not always necessary; often you just care about modifying some piece of your data structure, and you can leave the unchanging parts as shallow copies.


To your specific case: you are modifying a Client by replacing its orders array with a new array containing some subset of the original contents, and you are not changing those contents. So you really only need a shallow copy of a Client object. You don't need slice() at all since you don't care about copying the array (and filter() always returns a new array anyway). You want to do something like this:

private filter(){
            this.data = this.originalData.filter((client: Client) => {

                // Filter list of orders first
                let filteredListOfOrders = client.ListOfOrders.filter((ordersItem: any) => {
                    const searchStr = JSON.stringify(ordersItem).toLowerCase();
                    return searchStr.indexOf(this.filterValue.toLowerCase()) !== -1;
                });

                const clientCopy = Object.assign({}, client);
                // Assign filtered list of orders to copied client item 
                clientCopy.ListOfOrders = filteredListOfOrders;

                const searchStr = JSON.stringify(clientCopy).toLowerCase();
                return searchStr.indexOf(this.filterValue.toLowerCase()) !== -1;
            });
}

Note that I say "something like" this, because your code doesn't compile for me (a complete and verifiable example is always helpful). So you'll have to adjust the above to suit your needs.

Hope that helps; good luck!

Upvotes: 1

Related Questions