1
\$\begingroup\$

I created this type for use in a "Wizard"-style user interaction. In each step, the user chooses an option called a Modifier and each of those options contains subselections that must be made.

These Modifiers are stored with two parallel structures, one of the fixed values and a second of the selectable ones using this type. When the uses chooses a suboption, the chosen value is merged into the fixed values and when completed, the values are used to modify the wizard model.

/**
 *  A portion of a modifier that consists of a set of possible values and a number of them
 *  that will be selected. After selection, the selected values will be added to the set values
 *  of the modifier.
 */
export class SelectableModifier {

    constructor(numSelectionsNeeded:number, options:Array<any>, selectionTime:string) {
        this._numSelectionsNeeded = numSelectionsNeeded;
        this._options = options;
        this._selectionTime = selectionTime;
    }

    /**
     * The number of options that need to be selected.
     */
    private _numSelectionsNeeded:number;
    /**
     * The available options
     */
    private _options:Array<any>;

    private _selectionTime:string;

    /**
     * Choose from this selection, decomposing it into the selected options.
     *
     * @param chosenIndices
     */
    public makeSelection(chosenIndices:Array<Number>):Array<any> {
        if (chosenIndices.length != this._numSelectionsNeeded) {
            throw "The selection requires that " + this._numSelectionsNeeded + " selections be made but " + chosenIndices.length + " were instead."
        }
        return this._options.filter((element, index)=> {
            return chosenIndices.indexOf(index) !== -1;
        });
    }

    get numSelectionsNeeded():number {
        return this._numSelectionsNeeded;
    }

    get options():Array<any> {
        return this._options;
    }

    get selectionTime():string {
        return this._selectionTime;
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ I'm not completely sure if this is applicable for your use case, but if it is I highly recommend getting rid of any in favor of using a generic class. \$\endgroup\$
    – Gerrit0
    Commented Sep 8, 2017 at 19:55
  • 1
    \$\begingroup\$ @Gerrit0 The application was originally a Javascript project that I added TypeScript to. Additionally, in general there is no guarantee that the different options will be of a shared type. \$\endgroup\$ Commented Sep 8, 2017 at 20:52

1 Answer 1

1
\$\begingroup\$

How about this?

  • Use constructor(private _options: any[]) instead of field declaration and assignment.
  • As a defensive programming measure use = [] to initialize the array with an empty value in null/undefined was padded in.
  • Instead of numerous searches through chosenIndices (via indexOf) use a simple .map(...) function instead.

export class SelectableModifier {

  constructor(private _numSelectionsNeeded: number, private _options: any[] = [], private _selectionTime: string) { }

  get numSelectionsNeeded(): number {
    return this._numSelectionsNeeded;
  }

  get options(): any[] {
    return this._options;
  }

  get selectionTime(): string {
    return this._selectionTime;
  }

  public makeSelection(chosenIndices: number[]): any[] {
    if (chosenIndices.length != this.numSelectionsNeeded) {
      throw new Error("The selection requires that " + this._numSelectionsNeeded + " selections be made but " + chosenIndices.length + " were instead.");
    }

    return chosenIndices.map(indexOfChosen => this.options[indexOfChosen]);
  }
}

UPD1

If null/undefined choices should be treated as errors, you can check for their presence and throw.

public makeSelection(chosenIndices: number[]): any[] {
    if (chosenIndices.length != this.numSelectionsNeeded) {
        throw new Error(`The selection requires that ${this._numSelectionsNeeded} selections be made but ${chosenIndices.length} were instead.`);
    }

    const result = chosenIndices.map(indexOfChosen => this.options[indexOfChosen]);
    if (result.some(item => item == null))
        throw new Error('Selection can not be null/undefined.');
    return result;
}
\$\endgroup\$
8
  • \$\begingroup\$ I think I will throw an exception on an empty options argument, instances are generated from json files so empty options is a failure state. \$\endgroup\$ Commented Sep 8, 2017 at 21:08
  • 1
    \$\begingroup\$ @ThisIsNoZaku take a look at the UPD1. That should do the job if error throwing is desired. By the way, your original code didn't throw error either for the situation you described. \$\endgroup\$ Commented Sep 8, 2017 at 21:14
  • \$\begingroup\$ Soloydenk What is UPD1? \$\endgroup\$ Commented Sep 8, 2017 at 21:16
  • \$\begingroup\$ @ThisIsNoZaku it is an update I just made to my answer. :) \$\endgroup\$ Commented Sep 8, 2017 at 21:17
  • 1
    \$\begingroup\$ Also worth mentioning that you should throw errors, not strings. \$\endgroup\$
    – Gerrit0
    Commented Oct 5, 2017 at 4:33

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.