打字稿对象集

问题描述 投票:0回答:4

我是 Angular2 和 TypeScript 的新手。我在创建像 Set<> 这样的独特集合时遇到问题。 我想避免集合中重复的对象,为此目的,尝试使用 一组数据类型,如以下代码:

private cardItems = new Set<MyBean>([]);

MyBean 是一个对象。

export class MyBean  {

  id:integer
  ownerId:integer
  ownerName:string
  img: string;

constructor() {

}
public equals(obj: MyBean) {
    console.log(obj.id);
    if (this.id == obj.id) {
        console.log(obj.id);
        return true;
    }
    if (obj == null)
        return false;

    return true;
}

public hashCode(obj: MyBean) {
    return obj.id
}

}

但是 equals 和 hashCode 不会以这种方式运行。并且我在 set 中有重复的对象。

Set的实现方案是什么?

非常感谢

angular typescript collections set
4个回答
7
投票

如何扩展

Set
类,然后重写
add
方法:

interface SetItem {
    equals(other: SetItem): boolean;
}

class MySet<T extends SetItem> extends Set<T> {
    add(value: T): this {
        let found = false;
        this.forEach(item => {
            if (value.equals(item)) {
                found = true;
            }
        });

        if (!found) {
            super.add(value);
        }

        return this;
    }
}

操场上的代码


2
投票

实现Set的解决方案是什么

JavaScript

Set
使用与
===
相同的算法,并且无法在 JavaScript 类中覆盖

解决方案

您可以使用利用可重写函数的东西,例如https://github.com/basarat/typescript-collections使用

toString


0
投票

当两个对象相似时,您可以使用类似于比较的技术:

JSON.stringify(obj)

但是,这需要您在阅读时

JSON.parse()
您的集合中的成员,并且需要一些额外的打字。

const obj1 = {id: 1, data: 'blue'};
const obj2 = {id: 2, data: 'red'};
const obj3 = {id: 1, data: 'blue'};
const myStringifiedObjSet: Set<string> = new Set([]);

myStringifiedObjSet.add(JSON.stringify(obj1));
  // Set { '{"id":1,"data":"blue"}' }
myStringifiedObjSet.add(JSON.stringify(obj2));
  // Set { '{"id":1,"data":"blue"}', '{"id":2,"data":"red"}' }
myStringifiedObjSet.add(JSON.stringify(obj3));
  // Set { '{"id":1,"data":"blue"}', '{"id":2,"data":"red"}' }

myStringifiedObjSet.has(JSON.stringify(obj1));
  // true

const objArray: object[] = Array.from(myStringifiedObjSet).map(el => JSON.parse(el));
  // [ { id: 1, data: 'blue' }, { id: 2, data: 'red' } ]

0
投票

自定义

Set
实现还应该实现
has
等其他方法,并且应该保留 O(1) 查找和插入时间。

Set<string>
JSON.stringify(obj)
一起使用会丢失原始类型。

这是一种替代方案,使用 fast-equals 实现深度相等,使用 object-hash 实现 O(1) 查找和插入:

import { deepEqual } from 'fast-equals'
import objectHash, { type NotUndefined } from 'object-hash'

/**
 * TypeScript compares objects by reference, not by value.
 * The same behavior applies to Set, which is not unique by value.
 * This Set implementation uses value equality and preserves O(1) lookup and insertion time.
 */
class ValueSet<T extends NotUndefined> implements Set<T> {
  [Symbol.toStringTag]: string = 'ValueSet'

  private hashMap = new Map<string, T>()

  constructor(
    items: T[] = [],
    private toKey: (item: T) => string = objectHash,
  ) {
    for (const item of items) {
      this.add(item)
    }
  }

  get size(): number {
    return this.hashMap.size
  }

  has(item: T): boolean {
    const hash = this.toKey(item)
    const existingItem = this.hashMap.get(hash)
    return deepEqual(item, existingItem)
  }

  add(item: T): this {
    const hash = this.toKey(item)

    if (!this.hashMap.has(hash)) {
      this.hashMap.set(hash, item)
      return this
    }

    const existingItem = this.hashMap.get(hash)
    if (!deepEqual(item, existingItem)) {
      this.hashMap.set(hash, item)
    }
    return this
  }

  delete(item: T): boolean {
    const hash = this.toKey(item)
    const existingItem = this.hashMap.get(hash)
    if (deepEqual(item, existingItem)) {
      return this.hashMap.delete(hash)
    }
    return false
  }

  clear(): void {
    this.hashMap.clear()
  }

  forEach(
    callbackfn: (value: T, value2: T, set: Set<T>) => void,
    thisArg?: unknown,
  ): void {
    this.hashMap.forEach(value => {
      callbackfn.call(thisArg, value, value, this)
    })
  }

  [Symbol.iterator](): SetIterator<T> {
    return this.hashMap.values()
  }

  keys(): SetIterator<T> {
    // Keys and values of a Set are the same thing.
    return this.hashMap.values()
  }

  values(): SetIterator<T> {
    return this.hashMap.values()
  }

  entries(): SetIterator<[T, T]> {
    return new Set(this.hashMap.values()).entries()
  }
}

export default ValueSet

这里有一些使用 vitest 进行的测试(与 jest 相同的语法):

import { describe, expect, it } from 'vitest'
import ValueSet from '../ValueSet'

interface SomeType {
  foo: number
}

describe('ValueSet', () => {
  describe('constructor', () => {
    it('initializes the set with items', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }, { foo: 1 }])
      expect(set.size).toBe(2)
    })
  })

  describe('size', () => {
    it('returns the current number of items', () => {
      const set = new ValueSet<SomeType>()
      expect(set.size).toBe(0)

      set.add({ foo: 0 })
      expect(set.size).toBe(1)

      set.delete({ foo: 0 })
      expect(set.size).toBe(0)
    })
  })

  describe('has', () => {
    it('returns true for existing items', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }])

      expect(set.has({ foo: 0 })).toBe(true)
    })
    it('returns false for non-existent items', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }])

      expect(set.has({ foo: 1 })).toBe(false)
    })
  })

  describe('add', () => {
    it('adds unique items to the set', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }])
      set.add({ foo: 1 })

      expect(set.size).toBe(2)
      expect(set.has({ foo: 0 })).toBe(true)
      expect(set.has({ foo: 1 })).toBe(true)
    })

    it('ignores duplicate items', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }])
      set.add({ foo: 0 })

      expect(set.size).toBe(1)
      expect(set.has({ foo: 0 })).toBe(true)
    })

    it('overwrites items with the same hash/key but different value', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }], () => 'constant_hash')
      set.add({ foo: 1 })

      expect(set.size).toBe(1)
      expect(set.has({ foo: 0 })).toBe(false)
      expect(set.has({ foo: 1 })).toBe(true)
    })
  })

  describe('delete', () => {
    it('deletes existing items', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }, { foo: 1 }])

      expect(set.delete({ foo: 0 })).toBe(true)
      expect(set.size).toBe(1)
      expect(set.has({ foo: 0 })).toBe(false)
      expect(set.has({ foo: 1 })).toBe(true)
    })

    it('ignores non-existent items', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }])

      expect(set.delete({ foo: 1 })).toBe(false)
      expect(set.size).toBe(1)
      expect(set.has({ foo: 0 })).toBe(true)
    })
  })

  describe('clear', () => {
    it('removes all items from the set', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }, { foo: 1 }])
      expect(set.size).toBe(2)
      set.clear()
      expect(set.size).toBe(0)
    })
  })

  describe('forEach', () => {
    it('preserves the order of insertion', () => {
      const set = new ValueSet<SomeType>()
      set.add({ foo: 0 }).add({ foo: 2 }).add({ foo: 1 })

      const items: SomeType[] = []
      set.forEach(value => {
        items.push(value)
      })

      expect(items).toEqual([{ foo: 0 }, { foo: 2 }, { foo: 1 }])
    })
  })

  describe('for-of', () => {
    it('preserves the order of insertion', () => {
      const set = new ValueSet<SomeType>()
      set.add({ foo: 0 }).add({ foo: 2 }).add({ foo: 1 })

      const items: SomeType[] = []
      for (const item of set) {
        items.push(item)
      }

      expect(items).toEqual([{ foo: 0 }, { foo: 2 }, { foo: 1 }])
    })
  })

  describe('keys', () => {
    it('returns all items', () => {
      const items = [{ foo: 0 }, { foo: 1 }]
      const set = new ValueSet<SomeType>(items)

      expect(Array.from(set.keys())).toEqual(items)
    })
  })

  describe('values', () => {
    it('returns all items', () => {
      const items = [{ foo: 0 }, { foo: 1 }]
      const set = new ValueSet<SomeType>(items)

      expect(Array.from(set.values())).toEqual(items)
    })
  })

  describe('entries', () => {
    it('returns the entries with the item as key and value', () => {
      const set = new ValueSet<SomeType>([{ foo: 0 }, { foo: 1 }])

      expect(Array.from(set.entries())).toEqual([
        [{ foo: 0 }, { foo: 0 }],
        [{ foo: 1 }, { foo: 1 }],
      ])
    })
  })
})
© www.soinside.com 2019 - 2024. All rights reserved.