Home > Software design >  access property of nested object based on dynamic string
access property of nested object based on dynamic string

Time:01-27

I have this object model:

export class FrcCapacity {
  constructor(
    public id?: number,
    public frcId?: number,
    public capGroupId?: number,
    public capGroup?: CapGroup,
    public salesProductId?: number,
    public p1?: number,
    public p2?: number,
    public p3?: number,
    public p4?: number,
    public p5?: number,
    public p6?: number,
    public p7?: number,
    public p8?: number,
    public p9?: number,
    public p10?: number,
    public p11?: number,
    public p12?: number,
    public total?: number
  ) {}
}

And I have an array of this, f. e. frcCapacity, filled with objects of the above object model.

I want to write a function, where I want to set the px value of the processed object. For this, I have all needed data and the function body looks like this:

periodValueChange(val: string, rowIndex: number, field: string) {
    for (let [key, value] of Object.entries(this.frcCapacity[rowIndex])) {
      if (key === field) this.frcCapacity[rowIndex]???
    }
  }

I'm trying this with Object.entries, but what should I write in place of the ???. How can I access the px field based on the field string parameter?

After some thinking and searching, this solution works:

periodValueChange(val: string, rowIndex: number, field: string) {
    let frcCap = this.frcCapacity[rowIndex];
    let map = new Map(Object.entries(frcCap));
    for (let [key, value] of map) {
      if (key === field) {
        map.set(field,  val);
      }
    }
    let obj = Array.from(map).reduce(
      (obj, [key, value]) => Object.assign(obj, { [key]: value }),
      {}
    );
    this.frcCapacity[rowIndex] = obj;
  }

Basically, I needed something like this:

periodValueChange(val: string, rowIndex: number, field: string) {
    this.frcCapacity[rowIndex].field =  val;
  }

Where the field parameter can be p1, p2, etc.

CodePudding user response:

Since you've used string for both the property name (field) and the value (val), I'm going to assume that you're getting those strings from somewhere that you can't get non-strings from (like form fields). So there are two challenges:

  1. field could be an invalid property name for FrcCapacity objects, and

  2. val may not be a valid value for whatever property field identifies.

To handle this in a mostly-typesafe way, you'll need a function that validates that a key is a valid FrcCapacity key, such as a type assertion function:

function assertIsValidFrcCapacityKey(key: string): asserts key is keyof FrcCapacity {
    switch (key) {
        case "id":
        case "frcId":
        case "capGroupId":
        // ...and so on for all the valid property names...
            break;
        default:
            throw new Error(`${key} is not a valid key for FrcCapacity objects`);
    }
}

That tells TypeScript that if that function doesn't throw, the key passed in is a valid key for FrcCapacity objects. Obviously, it's less than ideal that you have to repeat all the property names, but there we are.

Then you have to handle the number / CapGroup thing, which you can hardcode into the function:

periodValueChange(val: string, rowIndex: number, field: string) {
    assertIsValidFrcCapacityKey(field);
    if (field === "capGroup") {
        frcCapacity[rowIndex].capGroup = convertStringToCapGroup(val);
    } else {
        frcCapacity[rowIndex][field] =  val;
    }
}

(In that example I've used unary to convert the val string to a number, but there are other ways; I list the various options and their pros and cons here. I've also left the implementation of convertStringToCapGroup to you, since I don't know what a CapGroup is, nor how you're encoding it as a string in your form.)

  •  Tags:  
  • Related