-
Notifications
You must be signed in to change notification settings - Fork 143
getRow(y) and getData() (ab)use
Bona fide use cases for getRow(y)
are rare; getValue(x, y)
is almost always preferred. Any use of getRow(y)
is suspect because it assumes a row-oriented data set with named columns. Calling getRow(y)
and consuming the result on a data model that does not match these assumptions is inefficient at best.
Recommendation: Data models should only implement getRow(y)
when an efficient implementation is possible. Otherwise, the default implementation injected by Hypergrid into data models lacking an implementation is sufficient and usually the best alternative (because of its "lazy" design).
The following advice pertains only to data models that do not implement getRow()
.
The default getRow
implementation is injected when the data model has no implementation of its own (along with all the methods marked "injected" in the API doc.
For performance, the getRow
fallback does not attempt to build a row object with column name key/value pairs on every call. Rather, it returns a proxy object with an accessor (getter/setter) for each column. The proxy object is (re)built by initDataRowProxy
whenever the schema changes.
getRow
simply sets the "hidden" (non-enumerable) property dataRowProxy.$y$
before returning dataRowProxy
itself. The getters are executed "lazily" (only as they are accessed).
This is fairly performant because typically only a small handful of columns are accessed in this way.
Caveat: This row accessor object has an important defect. All references to row objects gotten in this way are actually to the same object, all reflecting the current value of dataRowProxy.$y$
. We have eliminated most of Hypergrid's internal use cases for getRow
. In general, developers should try to avoid using getRow()
altogether. When it is necessary to work with data row objects, and in particular to retain more than one in memory at a time, the work-around is to clone the object with Object.assign({}, dataSource.getRow(y))
before accessing another row. This will however execute all the getters and at once to assemble a new object with primitive values, which is course the worse case scenario for a lazy object.
If you really need to use getRow()
and getData()
, consider using a data source that implements them, especially getData()
. Local data sources with row-oriented data and named columns should always implement both since the implementations are truly trivial and performant. For example, datasaur-local
's implementations are simply return this.data[y]
and return this.data
, respectively.
Data models should only implement getData()
when it is "convenient" to do so, when an efficient implementation is possible. Otherwise, the "lazy" default implementation may be the best alternative. The following advice pertains only to data models that do not implement getData()
.
getData
is not called by Hypergrid and should not be called by applications either. That said, if the default implementation is called it will return results. However, because it does copy all the data from every row, the developer is cautioned not to abuse it.
getData
should not be used as a general purpose way to inspect or search grid data. The reason for this advice is that applications should make no assumptions about how the underlying data model maintains its data. While data models that maintain an internal array of dataRow
objects will be performant (returning a reference to that array), other data models may have no such internal structure. Therefore, on each and every call to getData()
, such data models will construct a dataRow
object for each row of data, and construct an array to contain them. This obviously cannot be performant for any sizable grid as it requires copying the data.
Even when you know your data model is simply returning a reference to an internal data array, you are strongly advised against using getData
for any purpose other than exporting data. Should your application need to be retrofitted down the road with a data model that is not performant in this respect, you will have a serious performance problem.
The typical anti-pattern use case for calling getData
is to iterate through the rows to analyze or translate the data in some way. Do not do this! Such data analysis should never be performed at the application level but rather should be built into your data model.
Even using getData
for its intended purpose may be problematical for large numbers of rows, subject to memory constraints.