简体   繁体   English

Angular2 rxjs 按可观察字段排序(可观察)对象列表

[英]Angular2 rxjs sort (observable) list of objects by an observable field

I want to sort a list of things by an observable field, but can't wrap my head around observables to get this working.我想按可观察字段对事物列表进行排序,但无法围绕可观察对象进行排序以使其正常工作。 Has somebody an idea how to achieve this?有人知道如何实现这一目标吗?

The initial situation is something like the following:初始情况类似于以下内容:

Thing[] things;

interface Thing {
  name: Observable<string>
}
<ul>
  <li *ngFor="const thing for things">
    {{thing.name | async}}
  </li>
</ul>

Since I obviously haven't described my problem properly: The field I want to sort the list of things on is an Observable, not a plain string.因为我显然没有正确描述我的问题:我想要对事物列表进行排序的字段是一个 Observable,而不是一个普通的字符串。 I want to keep the field updated via websockets so to detect the changes properly I have to use an Observable field on which I can subscribe.我想通过 websockets 保持该字段的更新,以便正确检测更改,我必须使用我可以订阅的 Observable 字段。

Thanks for clarifying the question, Phosphoros.感谢您澄清问题,磷。 :) :)

Here's how you could do what you asked:以下是您可以按照您的要求执行的操作:

// Function to compare two objects by comparing their `unwrappedName` property.
const compareFn = (a, b) => {
  if (a.unwrappedName < b.unwrappedName)
    return -1;
  if (a.unwrappedName > b.unwrappedName)
    return 1;
  return 0;
};

// Array of Thing objects wrapped in an observable.
// NB. The `thing.name` property is itself an observable.
const thingsObs = Observable.from([
  { id: 1, name: Observable.of('foo') },
  { id: 2, name: Observable.of('bar') },
  { id: 3, name: Observable.of('jazz') }
]);

// Now transform and subscribe to the observable.
thingsObs

  // Unwrap `thing.name` for each object and store it under `thing.unwrappedName`.
  .mergeMap(thing =>
    thing.name.map(unwrappedName => Object.assign(thing, {unwrappedName: unwrappedName}))
  )

  // Gather all things in a SINGLE array to sort them.
  .toArray()

  // Sort the array of things by `unwrappedName`.
  .map(things => things.sort(compareFn))

  .subscribe();

Logging emitted values to the console will show an array of Thing objects sorted by their unwrappedName property:将发出的值记录到控制台将显示一个按其unwrappedName属性排序的 Thing 对象数组:

[
  { id: 2, name: ScalarObservable, unwrappedName: "bar" },
  { id: 1, name: ScalarObservable, unwrappedName: "foo" },
  { id: 3, name: ScalarObservable, unwrappedName: "jazz" }
]

Please let me know if you have questions about this code.如果您对此代码有疑问,请告诉我。

If I understand you correctly, you want to have an object that looks like this:如果我理解正确的话,你想要一个看起来像这样的对象:

Thing {
   name: string;
}

You then have want to have an Observable that holds on array of Thing :然后,您需要一个 Observable 来保存Thing数组:

things$: Observable<Thing[]>;

You then want to sort your things in the thing array by a property, in this case name .然后,您希望通过属性(在本例中为namething array中的thing array进行排序。 That could be done like this:可以这样做:

...

let sorted$: Observable<Thing[]> = things$.map(items => items.sort(this.sortByName))

...

sortByName(a,b) {
  if (a.name < b.name)
    return -1;
  if (a.name > b.name)
    return 1;
  return 0;
}

...

And then finally, like Toung Le showed in his answer, change your template like this:最后,就像Toung Le在他的回答中所展示的那样,像这样更改您的模板:

<ul>
  <li *ngFor="let thing of sorted$ | async">
    {{thing.name}} <!--No need async pipe here. -->
  </li>
</ul>

You can use Observable.map .您可以使用Observable.map For example:例如:

Observable<Thing[]> things;
sortedThings$ = things.map(items => items.sort()) // Use your own sort function here.

In your template:在您的模板中:

<ul>
  <li *ngFor="let thing of sortedThings$ | async">
    {{thing.name}} <!--No need async pipe here. -->
  </li>
</ul>

You can use Observable.map then sort() with localeCompare which would look something like this :您可以使用Observable.map然后sort()localeCompare ,它看起来像这样:

.map(data => ({
        label: data.name
}))
.sort((a, b) => a.label.localeCompare(b.label));

Use groupby operator (play with it ):使用groupby运算符(使用它):

const $things = getThings();

$things.pipe(
    groupBy(thing => thing.id),
    mergeMap(group$ => group$.pipe(
        reduce((acc, cur) =>[...acc, cur], [])
    ))
)
.subscribe(console.log)

Groupby docs . Groupby 文档

It is a pretty simple solution, once you understand how .pipe works and which Operators are there.这是一个非常简单的解决方案,一旦您了解.pipe工作原理以及那里有哪些Operator

If we convert all items to an array using toArray operator and pipe the whole array as a single item to mergeMap operator, where we'll be able to sort it and then breaks out the array back.如果我们使用toArray运算符将所有项目转换为数组,并将整个数组作为单个项目通过mergeMapmergeMap运算符,我们将能够对其进行排序,然后将数组mergeMap回来。

interface Thing {
  name: string;
}

const things: Observable<Thing>;

...

things
   .pipe(toArray())
   .pipe(mergeMap(_things => 
      _things.sort((a, b) => 
         a.name.localeCompare(b.name)
      )
   ));

// OR

things.pipe(toArray(), mergeMap(_things => 
   _things.sort((a, b) => 
      a.name.localeCompare(b.name)
   )
));

Changed the sort function with a filter function also made it work with rxjs 6.6.2使用过滤功能更改了排序功能也使其适用于 rxjs 6.6.2

https://stackblitz.com/edit/rxjs-ys7a9m?file=index.ts https://stackblitz.com/edit/rxjs-ys7a9m?file=index.ts

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM