Send many requests to check if name exists and set the name property

Viewed 70

I have an array of devices. For each device I want to set the default name ("DeviceX" - X its a sequence number) but some of them might be already used/occupied. So I need make an request to check if name already exists (ifDeviceExists() returns true or false in response). If the name is already occupied it should increase the number and once again send request to check if that name (e.g. "Device2") already exist. If name is not occupied then it should set name of that device (device.name = "Device2").

For example - occupied devices names: "Device2" and "Device4". Then the names of new created devices should be: "Device1", "Device3", "Device5", "Device6" ... So the devices array should be: [{name: "Device1"}, {name: "Device3"}, {name: "Device5"}, {name: "Device6"}, ... ]

The main problem for me here is that checking if name exists is async action and I do not know how to handle that (where to send request and when to subscribe) Could anyone help me with that?

3 Answers
  ifDeviceExists(name: string): Observable<boolean> {
    return [ 'Device2', 'Device4' ].includes(name) ? of(true) : of(false);
  }

  findNextDeviceName(indexWrapper: { index: number }, device: Device):
    Observable<{ device: Device, name:string; index: number }> {
    let currentName: string;
    let currentIndex = indexWrapper.index;
    return interval(0).pipe(
      // send requests to check name sequentially until it returns false and filter passes event
      // to first() operator which will stop interval loop, NOTE: if ifDeviceExists() allways returns true,
      // there will be infinite loop
      concatMap(interval => {
        currentIndex = currentIndex + interval;
        currentName = `Device${currentIndex}`;
        return this.ifDeviceExists(currentName);
      }),
      filter(exists => !exists),
      first(),
      map(() => ({device, name: currentName, index: currentIndex})),
    )
  }

  renameDevices(){
    const devices = [{id: 101}, {id: 102}, {id: 103}, {id: 104}];

    // this object keeps last index and allows to change it between concatMap iterations
    const indexWrapper = {index: 1};

    from(devices).pipe(
      concatMap(device => this.findNextDeviceName(indexWrapper, device)),
      map(result => {
        indexWrapper.index = result.index + 1;
        result.device.name = result.name;
        return result.device;
      }),
      toArray()
    ).subscribe({
      next: (devices) => {
        console.log('Devices with names', devices);
      }
    })
  }

As you didn't provide extra info,so I provided solution as per my understanding and assumption

  • I assumed ifDeviceExists returning Observable<boolean> and returning true/false as per availability status
  • Assumed expecting new array with device name attached

As ifDeviceExists returning observable,so in order to use it's result inside pipe we need to use higher order observable,in this case I used concatmap

About concatMap

For indexing used concatMap index value

  devices = [
    { id: 1, status: 'available' },
    { id: 2, status: 'used' },
    { id: 3, status: 'available' },
    { id: 4, status: 'occupied' },
    { id: 5, status: 'available' },
    { id: 6, status: 'available' },
    { id: 7, status: 'available' },
  ];
  
  ngOnInit(): void {
    this.setDevicesName().subscribe(console.log);
  }

  ifDeviceExists(deviceId: number): Observable<boolean> {
    let device = this.devices.find((d) => d.id === deviceId);
    return of(device.status === 'used' || device.status === 'occupied');
  }

  setDevicesName(): Observable<any> {
    return from(this.devices).pipe(
      concatMap((device, index) =>
        this.ifDeviceExists(device.id).pipe(
          tap(() => index++),
          filter((res) => !res),
          map((res) => {
            device['name'] = 'Device' + index;
            return device;
          })
        )
      ),
      toArray()
    );
  }

const reserved = ["device2", "device5"]; //any list that contains device<X> elements
const devices = [{},{},{},{},{},{},{},{},{},{}];
let num = 0;
for (let device of devices) {
  if (!reserved.includes("device" + num)) {
    device.name = 'device' + num
  };
  num++
}
console.log(devices)

Related