[英]RxJS: Count changes on collection items
給定一組項目,在任何項目/項目屬性的特定時間間隔內跟蹤/監視/計數變化的最佳方法是什么?
更具體地說,我需要能夠監視更改並通知特定的動態條件。
到目前為止我的解決方案:
每次發生變化時,我都會在 ReplaySubject 中推送數據更改日志。
我訂閱了這個主題並應用了一些自定義運算符來過濾特定列並計算更改。
前任。 '如果項目 Y 的屬性 X 在過去 5 秒內更改了 2 次,請通知我'
// const dataChangeLog$ = new ReplaySubject(...)
dataChangeLog$.pipe(
filterColumnChanges('columnName'), // filters only changes on the given column
windowTime(5000),
switchMap((change$) => change$.pipe(countCellChanges(2)))
);
const countCellChanges = (
limit: number,
reset = true
): MonoTypeOperatorFunction<DataChangedLogEntry> => {
return (source$) =>
defer(() => {
const counterMap = new Map<unknown, number>();
const getCellCounter = (dataChangeLog: DataChangedLogEntry) =>
counterMap.get(dataChangeLog.primaryKeyValue) ?? 0;
return source$.pipe(
tap((dataChangeLog) => {
let currentCounter = getCellCounter(dataChangeLog);
counterMap.set(dataChangeLog.primaryKeyValue, ++currentCounter);
}),
filter((dataChangeLog) => {
return getCellCounter(dataChangeLog) >= limit;
}),
// if limit is reached, reset it
tap((dataChangeLog) => {
if (reset) {
counterMap.set(dataChangeLog.primaryKeyValue, 0);
}
})
);
});
};
問題是計數器每 5 秒重置一次,無論我在此間隔內有多少更改。
我真正需要的是檢查每一次排放是否在最后 Y 秒內至少有 X 次排放。
該運算符檢查源並在您描述的條件滿足時立即發出。
const xInLastY = (x, y) => (source$) => {
const runningCount$ = source$.pipe(
mergeMap(() => {
const inc$ = of(1);
const dec$ = of(-1).pipe(delay(y));
return of(inc$, dec$).pipe(mergeAll());
}),
scan((acc, next) => acc + next, 0),
);
return runningCount$.pipe(
map((count) => count >= x),
distinctUntilChanged(),
filter(x => x)
);
};
編輯:專門解決這一點:
如果項目 Y 的屬性 X 在過去 5 秒內更改了 2 次,請通知我
操作員巧妙地融入了這個用例。
const recentChangesToProperty$ = source$.pipe(
distinctUntilKeyChanged(MY_PROPERTY),
xInLastY(2, 5e3)
);
最后更新:
這概括了“最后 X 毫秒的條件”的情況,以滿足 OP 的其他要求:
僅當更改的值是最后 x 毫秒內的最高/最低時才發出
我們不是累積一個增長/縮小的計數,而是累積一個增長/縮小的值數組。 “max”的情況在下面實現,“min”的實現很容易從這里得到。
type ArrayAction<T> = { type: 'PUSH'; value: T } | { type: 'POP' };
const arrayReducer = <T>(state: T[], action: ArrayAction<T>) => {
switch (action.type) {
case 'PUSH':
return [...state, action.value];
case 'POP':
return state.slice(1);
default:
return state;
}
};
const timeTrailingValues = (ms: number) => <T>(
source$: Observable<T>
): Observable<T[]> => {
const runningList$ = source$.pipe(
mergeMap((value) => {
const in$ = of({ type: 'PUSH', value });
const out$ = of({ type: 'POP' }).pipe(delay(y));
return of(in$, out$).pipe(mergeAll());
}),
scan(arrayReducer, [])
);
};
const maxInLastX = (ms: number) => <T>(
source: Observable<T>
): Observable<boolean> =>
source$.pipe(
timeTrailingValues(ms),
map((xs) => {
const [latestValue] = xs.slice(-1);
const rest = xs.slice(0, -1);
return rest.every((value) => value <= latestValue);
}),
filter(isMax => isMax)
);
// Still want to track the running count? Combine timeTrailingValues with this
const atSize = (x: number) => <T>(
source$: Observable<T[]>
): Observable<boolean> => {
return source$.pipe(
map((xs) => xs.length >= x),
distinctUntilChanged(),
filter((x) => x)
);
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.