【问题标题】:Mapping data to Observable Array 1 based on field value from Observable Array 2根据 Observable Array 2 中的字段值将数据映射到 Observable Array 1
【发布时间】:2019-12-29 14:21:25
【问题描述】:

我正在尝试根据来自 arr2 的字段值将数据映射到 arr1,并且遇到了请求数据顺序的问题。 我在 package.json 文件中使用下面列出的以下语言。

为了(希望)更好地解释我正在尝试做的事情:有两个数组数组(例如 arr1 = { Locations[]} 和 arr2 = {Activated Locations[]),其中基本数组是 Firestore 集合并嵌套文档是内部数组 - 适用于 arr1 和 arr2。所以内部数组包含一个字段“activatedId”,这是我试图用作确定 arr1 的内部数组是否包含映射为“激活”的字段的公共元素:真(或假)。 另外,我相信很多人会嘲笑我的代码,因为我刚刚接触到大多数语言:) 所以请客气!感谢您提供的任何帮助,如果您对如何更有效地完成此任务有任何建议,我很想听听您的意见。

感谢菜鸟!

    export class LocationListingModel{
      id: string;
      title: string;
      activateId?: string;
      activated: boolean;
    }
    export class ActiveLocationsModel {
      id: string;
      title: string;
      activateId?: string;
    }
private  activatedCheck(locationId: string, activatedId: string): boolean {
const locRef = this.afs.doc('locations/' + locationId);
const check = this.afs.collection<LocationDealListingModel>('activatedLocations/', ref => ref
.where('locRef', '==', locRef.ref)
.where('redeemId', '==', activatedId)).snapshotChanges();
if (check.subscribe(res => res.length > 0 )){
  return true;

} else {
  return false;
};
}



 public getsActivatedLocationDataSource(locationId: string, userId: string): Observable<Array<LocationListingModel>> {
let activated: boolean;
const locRef = this.afs.doc('locations/' + locationId);


return this.afs.collection<AvailableDealModel>('locations/', ref => ref.where('locRef', '==', locRef.ref))
  .valueChanges({ idField: 'id' })
  .pipe(map(actions => actions
    .map(data => {
      const locRef = this.afs.doc('userProfile/' + userId);
      const checkExists = this.afs.collection<LocationDealListingModel>('activeLocations/', ref => ref
        .where('locRef', '==', locRef.ref)
        .where('activeId', '==', data.activeId)).snapshotChanges();
      if (checkExists && (checkExists.subscribe(res => res.length > 0))) {
        activated = true;
      } else if (checkExists && (checkExists.subscribe(res => res.length === 0))) {
        activated = false;
      } else {
        console.log('error completing check. ')
      }

      const id = data.id;
      return { id, active: activated, ...data } as LocationListingModel;

    })
  ));

}

或者使用第一个函数-activatedCheck()

 public getsActivatedLocationDataSource(locationId: string, userId: string): Observable<Array<LocationListingModel>> {
let activated: boolean;
const locRef = this.afs.doc('locations/' + locationId);


return this.afs.collection<AvailableDealModel>('locations/', ref => ref.where('locRef', '==', locRef.ref))
  .valueChanges({ idField: 'id' })
        .pipe(map(actions => actions
    .map(data => {
      const checkExists = this.activatedCheck(locationId, activatedId);
      if (checkExists === true) {
        activated = true;
      } else if (checkExists === false) {
        activated = false;
      } else {
        console.log('error completing check. ')
      }
      const id = data.id;
      return { id, active: activated, ...data } as LocationListingModel;
    })
  ));

package.json

  "dependencies": {
    "@agm/core": "^1.0.0-beta.6",
    "@agm/snazzy-info-window": "^1.0.0-beta.6",
    "@angular/animations": "8.2.1",
    "@angular/cdk": "~8.1.3",
    "@angular/common": "8.2.1",
    "@angular/core": "8.2.1",
    "@angular/fire": "^5.2.1",
    "@angular/forms": "8.2.1",
    "@angular/material": "^8.1.3",
    "@angular/platform-browser": "8.2.1",
    "@angular/platform-browser-dynamic": "8.2.1",
    "@angular/pwa": "~0.802.1",
    "@angular/router": "8.2.1",
    "@angular/service-worker": "8.2.1",
    "@capacitor/android": "^1.0.0",
    "@capacitor/cli": "^1.0.0",
    "@capacitor/core": "^1.0.0",
    "@capacitor/ios": "^1.0.0",
    "@ionic-native/browser-tab": "^5.8.0",
    "@ionic-native/call-number": "^5.8.0",
    "@ionic-native/core": "^5.8.0",
    "@ionic-native/date-picker": "^5.8.0",
    "@ionic-native/facebook": "^5.8.0",
    "@ionic-native/geolocation": "^5.12.0",
    "@ionic-native/google-plus": "^5.12.0",
    "@ionic-native/ionic-webview": "^5.8.0",
    "@ionic-native/keyboard": "^5.8.0",
    "@ionic-native/native-storage": "^5.12.0",
    "@ionic-native/network": "^5.8.0",
    "@ionic-native/splash-screen": "^5.8.0",
    "@ionic-native/status-bar": "^5.8.0",
    "@ionic/angular": "^4.7.4",
    "@ionic/storage": "^2.2.0",
    "@logisticinfotech/ionic4-datepicker": "^1.4.1",
    "@ngrx/store": "^8.2.0",
    "@ngx-translate/core": "^11.0.1",
    "@ngx-translate/http-loader": "^4.0.0",
    "@types/jest": "^24.0.17",
    "angular-pipes": "^9.0.2",
    "angular-star-rating": "^4.0.0-beta.3",
    "angularjs-datepicker": "^2.1.23",
    "cordova-plugin-browsertab": "^0.2.0",
    "cordova-plugin-call-number": "^1.0.1",
    "cordova-plugin-datepicker": "^0.9.3",
    "cordova-plugin-facebook4": "^4.2.1",
    "cordova-plugin-geolocation": "^4.0.2",
    "cordova-plugin-googleplus": "^7.0.2",
    "cordova-plugin-keyboard": "^1.2.0",
    "cordova-plugin-nativestorage": "^2.3.2",
    "cordova-plugin-splashscreen": "^5.0.2",
    "cordova-plugin-statusbar": "^2.4.2",
    "cordova-sqlite-storage": "^3.2.1",
    "core-js": "^2.5.7",
    "dayjs": "1.8.0",
    "firebase": "^6.4.0",
    "geofire": "^5.0.1",
    "geofirex": "0.0.6",
    "google-libphonenumber": "^3.2.1",
    "hammerjs": "^2.0.8",
    "moment": "^2.24.0",
    "ng2-file-upload": "^1.3.0",
    "ngx-paypal": "^5.0.0",
    "promise-polyfill": "^8.1.3",
    "rxfire": "^3.6.8",
    "rxjs": "6.5.2",
    "rxjs-compat": "^6.5.2",
    "snazzy-info-window": "^1.1.1",
    "tslib": "^1.9.0",
    "videogular2": "^7.0.1",
    "zone.js": "^0.9.1"
  },
  "devDependencies": {
    "@angular-devkit/architect": "^0.802.1",
    "@angular-devkit/build-angular": "~0.802.1",
    "@angular-devkit/core": "^8.2.1",
    "@angular-devkit/schematics": "^8.2.1",
    "@angular/cli": "^8.2.1",
    "@angular/compiler": "8.2.1",
    "@angular/compiler-cli": "8.2.1",
    "@angular/language-service": "8.2.1",
    "@commitlint/cli": "^8.1.0",
    "@commitlint/config-angular": "^8.1.0",
    "@ionic/angular-toolkit": "^2.0.0",
    "@ionic/lab": "2.0.7",
    "@types/core-js": "^2.5.0",
    "@types/googlemaps": "^3.36.2",
    "@types/node": "12.0.0",
    "@webcomponents/webcomponentsjs": "^2.2.10",
    "codelyzer": "^5.0.1",
    "husky": "^1.3.1",
    "ts-node": "~8.1.0",
    "tslint": "~5.16.0",
    "typescript": "~3.5.3"
  }

【问题讨论】:

  • 恐怕你的问题不清楚。您的代码中有太多移动部分,其中许多根本不起作用,并且您的最终目标并不完全清楚。两个数组之间的“公共字段”是什么意思?如arr1[0] vs arr2[0]?这两个数组是由 Observables 发出的吗?
  • 部分。有两个数组数组(例如 arr1 = { Locations[]} 和 arr2 = {Activated Locations[]),其中基本数组是 Firestore 集合,嵌套文档是内部数组 - 适用于 arr1 和 arr2。所以内部数组包含一个字段“activatedId”,这是我试图用作确定 arr1 的内部数组是否包含映射为“激活”的字段的公共元素:真(或假)。希望清除它。这让我很头疼,而且我不太擅长向真实的人解释事情,哈哈。
  • 所以,如果我做对了,对于LocationListingModel[] 数组中的每个元素,您需要检查ActiveLocationsModel[] 数组中是否有任何元素与activatedId 值匹配,如果是这样,请将 LocationListingModelactivated 值设置为 true?
  • 我支持 Will - 很难确定您要完成什么。但是,如果您希望 Observable 链中的每个运算符都可以使用这两个数组,那么this answer 可能会有所帮助。
  • @WillAlexander 完全正确。

标签: arrays angular rxjs google-cloud-firestore angularfire


【解决方案1】:

所以这里有一大段代码,我认为它接近你想要的:

/*
* Dummy data to check everything with
*/
const locations = [
  { activatedId: '1234', activated: false },
  { activatedId: '2345', activated: false },
  { activatedId: '3456', activated: false },
  { activatedId: '4567', activated: false },
  { activatedId: '5678', activated: false },
  { activatedId: '6789', activated: false },
];
const activatedLocations = [
  { activatedId: '1234' },
  { activatedId: '3456' },
  { activatedId: '6789' }
];

/*
* Next we map the activatedLocations Array to an Object
* whose keys are the activatedIds and whose values are true
*/
const indexedActivatedLocations$ = of(activatedLocations).pipe(
  map(locs => {
    const index = {};
    for (let loc of locs) {
      index[loc.activatedId] = true;
    }
    return index;
    // index: { '1234': true, '3456': true, '6789' true }
  })
);

/*
* Finally, we use forkJoin to subscribe to both Observables and
* emit their results as an Array.
* Comparing each location.activatedId to the new Object's keys
* is much faster than searching through an Array again and again
* We change location.activated where necessary and then
* emit the updated Array.
*/
const updatedLocations$ = forkJoin({
  locations: of(locations),
  activated: indexedActivatedLocations$
}).pipe(
  map(data => {
    for (let loc of data.locations) {
      if (data.activated[loc.activatedId]) {
        loc.activated = true;
      }
    }
    return data.locations;
  })
);

【讨论】:

  • 看起来非常接近!我在虚拟数据上看到了我希望得到的结果,但是一旦我添加到 afs 集合中 for(let loc of locs) 就会引发错误“Type 'Observable' must have a '[Symbol.iterator ]()' 方法,返回一个迭代器”,在 for(let of) 上都有
  • 如果您考虑一下,那是有道理的。我创建了一个发出数组的 Observable,你需要做的就是用发出数组的 Observable 替换它。
  • 你是圣人先生。我欠你的。
  • 不要忘记接受答案以帮助其他有同样问题的人:)
猜你喜欢
  • 2017-09-23
  • 2015-02-03
  • 2017-12-13
  • 1970-01-01
  • 2019-04-07
  • 2018-11-09
  • 1970-01-01
  • 2018-03-04
相关资源
最近更新 更多