RxJS distinctUntilChanged - 对象比较

时间:2016-05-11 20:11:26

标签: angular reactive-programming rxjs

我有对象流,我需要比较当前对象是否与之前的对象不同,在这种情况下发出新值。我发现distinctUntilChanged操作符应该完全符合我的要求,但由于某种原因,除了第一个之外它永远不会发出值。如果我删除distinctUntilChanged值正常发生。

我的代码:

export class SettingsPage {
    static get parameters() {
        return [[NavController], [UserProvider]];
    }

    constructor(nav, user) {
        this.nav = nav;
        this._user = user;

        this.typeChangeStream = new Subject();
        this.notifications = {};
    }

    ngOnInit() {

        this.typeChangeStream
            .map(x => {console.log('value on way to distinct', x); return x;})
            .distinctUntilChanged(x => JSON.stringify(x))
            .subscribe(settings => {
                console.log('typeChangeStream', settings);
                this._user.setNotificationSettings(settings);
            });
    }

    toggleType() {
        this.typeChangeStream.next({
            "sound": true,
            "vibrate": false,
            "badge": false,
            "types": {
                "newDeals": true,
                "nearDeals": true,
                "tematicDeals": false,
                "infoWarnings": false,
                "expireDeals": true
            }
        });
    }

    emitDifferent() {
        this.typeChangeStream.next({
            "sound": false,
            "vibrate": false,
            "badge": false,
            "types": {
                "newDeals": false,
                "nearDeals": false,
                "tematicDeals": false,
                "infoWarnings": false,
                "expireDeals": false
            }
        });
    }
}

9 个答案:

答案 0 :(得分:19)

我终于找出问题所在。问题出在RxJS的版本中,在V4中,之前的参数顺序与V5不同。

RxJS 4:

  

distinctUntilChanged = function(keyFn,comparer)

RxJS 5:

  

distinctUntilChanged = function(comparer,keyFn)

在今天的每个文档中,您都可以找到V4参数顺序,请注意!

答案 1 :(得分:19)

我遇到了同样的问题,并使用JSON.stringify来比较对象:

.distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))

脏但工作的代码。

答案 2 :(得分:2)

无论如何,只要在应用程序中包含lodash,就可以简单地使用lodash的isEqual()函数,该函数进行了深入的比较,并且与distinctUntilChanged()的签名完全匹配:

.distinctUntilChanged(isEqual),

或者如果您有_可用(现在不建议这样做):

.distinctUntilChanged(_.isEqual),

答案 3 :(得分:1)

您还可以包装原始的distinctUntilChanged函数。

function distinctUntilChangedObj<T>() {
  return distinctUntilChanged<T>((a, b) => JSON.stringify(a) === JSON.stringify(b));
}

这使您可以像原始版本一样使用它。

$myObservable.pipe(
  distinctUntilChangedObj()
)

答案 4 :(得分:1)

如果可观察对象实际上发出相同对象的修改版本,则建议深度比较的答案都失败了,因为传递给比较的“最后”值将与当前,每次。

我们使用 BehaviorSubject 来实现这一点,碰巧每次都将相同的对象传递给 .next()。

在这种情况下,无论您使用什么比较函数,都无法使用默认的 distinctUntilChanged 解决方案。

答案 5 :(得分:0)

Mehdi的解决方案虽然速度很快,但是如果不维护订单将无法使用。使用deep-equalfast-deep-equal库之一:

.distinctUntilChanged((a, b) => deepEqual(a, b))

答案 6 :(得分:0)

如果您可变地更改该值,则其他答案均无效。如果需要使用可变数据结构,则可以使用 distinctUntilChangedImmutable ,它会深度复制前一个值,并且如果未传递任何比较函数,则会断言前一个值和当前值彼此相等(不是===断言)。

答案 7 :(得分:0)

来自RxJS v6 + 有一个与众不同的UntilKeyChanged

https://www.learnrxjs.io/operators/filtering/distinctuntilkeychanged.html

const source$ = from([
  { name: 'Brian' },
  { name: 'Joe' },
  { name: 'Joe' },
  { name: 'Sue' }
]);

source$
  // custom compare based on name property
  .pipe(distinctUntilKeyChanged('name'))
  // output: { name: 'Brian }, { name: 'Joe' }, { name: 'Sue' }
  .subscribe(console.log);

答案 8 :(得分:0)

您也可以制作自己的过滤器:

function compareObjects(a: any, b: any): boolean {
  return JSON.stringify(a) === JSON.stringify(b);
}

export function distinctUntilChangedObject() {
  return function<T>(source: Observable<T>): Observable<T> {
    return new Observable(subscriber => {
      let prev: any;
      let first = true;
      source.subscribe({
        next(value) {
          if (first) {
            prev = value;
            subscriber.next(value);
            first = false;
          } else if (!compareObjects(prev, value)) {
            prev = value;
            subscriber.next(value);
          }
        },
        error(error) {
          subscriber.error(error);
        },
        complete() {
          subscriber.complete();
        }
      });
    });
  };
}