2

I have a method that replaces one object in an array with another one. It will search by ID and change all records that match by the designated ID.

I would like to change this to a generic, reusable method that can operate in the same way for any object type I pass in. (EG: car.id, car.door.id, car.trunkMonkey.id, etc.)

Is there a way for me to pass the "element.car.id" path as a variable into this method?

updateArrayObject(item: any, data: any[]) {
        // Parse all objects in the array, looking for matches by ID
        data.forEach(element => {
            if (element.car.id === item.id) { // <-- Can I pass "element.car.id" into the method somehow?
                // Item ID matches. Replace the item.
                element.car = item;
            }
        });
    }
1

2 Answers 2

3

Some programs do this via string ("element.car.id") and parse the path at runtime. Not type-safe, unfortunately.

This here is a little more complicated and has its limits, but it is type-safe:

function updateArrayObject<T, R>( // ElementType, ReplacementType
    data: T[], // The array
    getter: (element: T) => R, // Getter of the thing that might be replaced
    setter: (element: T, replacement: R) => void, // Setter of the replacement, when appropriate
    keyComparer: (candidate: R, replacement: R) => boolean, // The matching predicate
    replacement: R) { // The replacement

    data.forEach(element => {
        if (keyComparer(getter(element), replacement)) {
            setter(element, replacement);
        }
    });
}

var sample: Element[] = [ /* ... */ ];

// Your car example
updateArrayObject<Element, Car>(
    sample,
    e => e.car,
    (e, r) => { e.car = r },
    (left, right) => left.id === right.id,
    {
        id: 42,
        door: { id: 0, name: 'driver' },
        trunkMonkey: { id: 0, tmName: 'bozo' }
    })

// Your trunkMonkey example
updateArrayObject<Element, TrunkMonkey>(
    sample,
    e => e.car.trunkMonkey,
    (e, r) => { e.car.trunkMonkey = r },
    (left, right) => left.id === right.id,
    {
         id: 0, tmName: 'bozo'
    })

// The various custom types involved.
interface Door { id: number, name: string }
interface TrunkMonkey { id : number, tmName: string }
interface Car {
    id: number,
    door: Door,
    trunkMonkey: TrunkMonkey
}
interface Element {
    car: Car,
    otherElementData: any
}

You might also research "functional lenses".

Sign up to request clarification or add additional context in comments.

1 Comment

You''re right. This was more complicated than I was hoping for, but it seems to be working fine for all my needs. Bozo the Trunk Monkey thanks you!
0

here is your generic function , cible can be car or door ... :

updateArrayObject(item: any, data: any[], cible) {
        // Parse all objects in the array, looking for matches by ID
        data.forEach(element => {
            if (element[cible].id === item.id) { // <-- Can I pass "element.car.id" into the method somehow?
                // Item ID matches. Replace the item.
                element[cible]= item;
            }
        });
    }

4 Comments

"your array won't be updated because element is a clone of his brother in data array"? forEach() doesn't clone and he isn't replacing the element, he's replacing the value of a member on the element - should be fine.
This looks like what I was attempting to do, but I am having difficulty figuring out how to call it. What do you pass in as the "cible" variable?
This is similar to passing a string
@Rethic call it like this updateArrayObject(item , data , "car") sorry for being late

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.