У меня есть этот dtsjs dataTable с двумя столбцами, одним из которых является число. И мне нужно добавить третий столбец с тем же номером, но выраженный в процентах от общего числа:
VENDOR SCORE PERCENT
------- ----- -------
Charles 5 50.0 %
Sarah 4 40.0 %
John 1 10.0 %
Если таблица была статичной, я бы просто вычислил проценты и добавил их в массив данных, прежде чем передавать его в Crossfilter:
let sum = data.reduce((accum, d) => accum + d.score, 0);
data.forEach(d => { d.percent = 100 * d.score / sum; });
Но данные могут быть отфильтрованы (с использованием диаграмм dc.js и selectMenus), например, пользователь может отображать только женщин-продавцов, а затем в таблице будет отображаться только Сара... но со своим старым процентом, 40% вместо пересчета это до 100%.
Есть ли способ обойти это? Я думаю, нет никакого способа автоматически вычислить процент. Но, может быть, я могу добавить слушателя к некоторому событию, которое запускается после фильтрации данных, но до того, как dc.js перерисовывает таблицу?
Кстати, в случае, если это имеет значение... чтобы ухудшить ситуацию, я не работаю непосредственно с измерением данных, вместо этого я передаю поддельную группу в таблицу (код, заимствованный отсюда):
function filteredGroup(originalGroup, filterFunction) {
return {
all: () => originalGroup.all().filter(filterFunction),
top: n => originalGroup.top(Infinity).filter(filterFunction).slice(0, n),
bottom: n => originalGroup.top(Infinity).filter(filterFunction).slice(-n).reverse()
};
}
let ndx = crossfilter(data);
let dim = ndx.dimension(d => d.vendor);
let grp = filteredGroup(dim.group().reduceCount(), d => d.value > 0);
dc.dataTable('#id')
.dimension(grp)
.group(d => d.someOtherField)
.showGroups(true)
.columns([d => d.vendor, d => d.score])
...
(Мой фактический массив данных будет содержать 5 записей для Charles, 4 для Sarah и 1 для John. Каждый из них содержит дополнительную информацию, такую как дата и время, которое используется в selectMenus для фильтрации. Но мне нужны агрегированные данные в таблице. Вот почему я использую группу вместо измерения.)
На самом деле это очень легко сделать. Я потерял перспективу, "группа" - не таинственная вещь, которая работает волшебным образом. Это просто объект с тремя функциями: all
, который возвращает массив со всеми данными, а также top
и bottom
, которые делают то же самое, но возвращают только первые N или последние N элементов. И, как указал Гордон в своем комментарии, эти функции вызываются каждый раз, когда отображается таблица.
Итак, мне просто нужно было вычислить мои проценты внутри определения поддельной группы:
function filteredGroup(originalGroup, filterFunction) {
return {
all: () => calc(originalGroup.all().filter(filterFunction)),
top: n => calc(originalGroup.top(Infinity).filter(filterFunction)).slice(0, n),
bottom: n => calc(originalGroup.top(Infinity).filter(filterFunction)).slice(-n).reverse()
};
}
function calc(data) {
let sum = data.reduce((accum, d) => accum + d.score, 0);
data.forEach(d => { d.percent = 100 * d.score / sum; });
return data;
}