1. Implementing Multi-Valued Reference Properties

A multi-valued reference property, such as the property Book::authors, allows storing a collection of references to objects of some type, such as Author objects. When creating a new object of type Book, the constructor function needs to have a parameter for providing a suitable value for this property. We can allow this value to be either a collection of internal object references or of ID references, as shown in the following example:

class Book {
  constructor ({isbn, title, year, authors, authorIdRefs, 
      publisher, publisher_id}) {
    this.isbn = isbn;
    this.title = title;
    this.year = year;
    // assign object reference or ID reference
    this.authors = authors || authorIdRefs;
    if (publisher || publisher_id) this.publisher = publisher || publisher_id;
  }
  ...
}

Notice that the constructor's parameter record is expected to contain either an authors or an authorIdRefs slot. The JavaScript expression authors || authorIdRefs, using the disjunction operator ||, evaluates to a map authors, if there is a slot with name authors, or to an array authorIdRefs, otherwise. We handle the resulting ambiguity in the property setter by checking the type of the argument as shown in the following code fragment:

set authors( a) {
  this._authors = {};
  if (Array.isArray(a)) {  // array of IdRefs
    for (let idRef of a) {
      this.addAuthor( idRef);
    }
  } else {  // map of IdRefs to object references
    for (let idRef of Object.keys( a)) {
      this.addAuthor( a[idRef]);
    }
  }
}

In JS, a collection-valued reference property can be implemented in two ways:

  1. having an array list (a JS array) of object references as its value,

  2. having a map as its value, such that the values of the object's standard ID attribute are the keys, which are mapped to internal JS object references.

We prefer using maps for implementing set-valued reference properties since they guarantee that each element is unique, while with an array we would have to prevent duplicate elements. Also, an element of a map can be easily deleted (with the help of the delete operator), while this requires more effort in the case of an array. However, for implementing ordered (or nonunique) association ends corresponding to ordered-collection-valued (or bag/sequence-valued) reference properties, we use JS arrays.