如何在javascript中实现映射或排序集

How to implement a map or sorted-set in javascript

本文关键字:映射 排序 实现 javascript      更新时间:2023-09-26

Javascript有使用数字索引["john", "Bob", "Joe"]的数组和可以像关联数组或允许对象值{"john" : 28, "bob": 34, "joe" : 4}的字符串键的"映射"一样使用的对象。

在PHP中,A)按值排序(同时维护键)和B)测试关联数组中是否存在值都很容易。

$array = ["john" => 28, "bob" => 34, "joe" => 4];
asort($array); // ["joe" => 4, "john" => 28, "bob" => 34];
if(isset($array["will"])) { }

您将如何在Javascript中实现此功能?

这是对加权列表或排序集之类的东西的常见需求,在这些东西中,您需要在数据结构中保留一个值的单个副本(如标签名称),还需要保留一个加权值。

这是我迄今为止想出的最好的:

function getSortedKeys(obj) {
    var keys = Object.keys(obj);
    keys = keys.sort(function(a,b){return obj[a]-obj[b]});
    var map = {};
    for (var i = keys.length - 1; i >= 0; i--) {
      map[keys[i]] = obj[keys[i]];
    };
    return map;
}
var list = {"john" : 28, "bob": 34, "joe" : 4};
list = getSortedKeys(list);
if(list["will"]) { }

看看Luke Schafer的回答,我想我可能已经找到了一种更好的方法来处理这个问题,方法是扩展Object.prototype:

// Sort by value while keeping index
Object.prototype.iterateSorted = function(worker, limit)
{
    var keys = Object.keys(this), self = this;
    keys.sort(function(a,b){return self[b] - self[a]});
    if(limit) {
        limit = Math.min(keys.length, limit);
    }
    limit = limit || keys.length;
    for (var i = 0; i < limit; i++) {
        worker(keys[i], this[keys[i]]);
    }
};
var myObj = { e:5, c:3, a:1, b:2, d:4, z:1};
myObj.iterateSorted(function(key, value) {
    console.log("key", key, "value", value)
}, 3);

http://jsfiddle.net/Xeoncross/kq3gbwgh/

使用ES6,您可以选择使用sort方法扩展Map构造函数/类,该方法采用可选的比较函数(就像数组一样)。sort方法将采用两个参数,每个参数都是键/值对,因此可以对键或值(或两者)进行排序。

sort方法将依赖于Maps的记录行为,即按插入顺序迭代条目。因此,这种新方法将根据排序顺序访问条目,然后删除并立即重新插入它们。

以下是它的样子:

class SortableMap extends Map {
    sort(cmp = (a, b) => a[0].localeCompare(b[0])) {
        for (const [key, value] of [...this.entries()].sort(cmp)) {
            this.delete(key);
            this.set(key, value); // New keys are added at the end of the order
        }
    }
}
// Demo
const mp = new SortableMap([[3, "three"],[1, "one"],[2, "two"]]);
console.log("Before: ", JSON.stringify([...mp])); // Before
mp.sort( (a, b) => a[0] - b[0] ); // Custom compare function: sort numerical keys
console.log(" After: ", JSON.stringify([...mp])); // After

我不知道为什么这些答案都没有提到内置JS类Set的存在。似乎是ES6的添加,也许这就是为什么。

理想情况下,覆盖下面的addkeys。。。覆盖keys的NB甚至不需要访问Set对象的原型。当然,您可以为整个Set类重写这些方法。或者创建一个子类SortedSet

  const mySet = new Set();
  const mySetProto = Object.getPrototypeOf(mySet);
  const addOverride = function(newObj){
    const arr = Array.from(this);
    arr.add(newObj);
    arr.sort(); // or arr.sort(function(a, b)...)
    this.clear();
    for(let item of arr){
      mySetProto.add.call(this, item);
    }
  }
  mySet.add = addOverride;
    
  const keysOverride = function(){
    const arr = Array.from(this);
    arr.sort(); // or arr.sort(function(a, b)...)
    return arr[Symbol.iterator]();
  }
  mySet.keys = keysOverride;

用法:

mySet.add(3); mySet.add(2); mySet.add(1); mySet.add(2);
for(let item of mySet.keys()){console.log(item)};

打印输出:

1。。。2.3

NB Set.keys()返回的不是Set中的项,而是迭代器。您可以选择返回已排序的数组,但这显然会破坏类的";合同";。

要覆盖哪一个?取决于您的使用情况和Set的大小。如果同时覆盖这两个,则会复制排序活动,但在大多数情况下,这可能无关紧要。

NB我建议的add函数当然是幼稚的;初稿":每次add时重建整个集合可能非常昂贵。基于检查CCD_,或者确定的某种其他方法,其中添加要添加的候选者(我说"候选者",因为如果已经发现存在"相同"元素,即其本身,它将被拒绝)。

这个问题还询问了排序地图的类似安排。。。事实上,ES6有一个新的Map类,可以进行类似的处理。。。而且Set只是一个专门的Map,正如你所料。

*例如。https://github.com/Crizstian/data-structure-and-algorithms-with-ES6/tree/master/10-chapter-Binary-Tree

通常不会对对象进行排序。但如果您这样做:按属性值对JavaScript对象进行排序

如果你想对数组进行排序,比如下面的

var arraylist = [{"john" : 28},{ "bob": 34},{ "joe" : 4}];

您可以随时使用Array.prototype.sort函数。来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

也许这段代码看起来像你想要的:

Object.prototype.asort = function(){
    var retVal = {};
    var self = this;
    var keys = Object.keys(this);
    keys = keys.sort(function(a,b){return self[a] - self[b]});
    for (var i = 0; i < keys.length; i++) {
        retVal[keys[i]] = this[keys[i]];
    }
    return retVal;
}
var map = {"john" : 28, "bob": 34, "joe" : 4}
var sortedMap = map.asort();//sortedMap["will"]: undefined

如果你使用开源项目jinqJs它很容易。

参见Fiddler

var result = jinqJs()
                .from([{"john" : 28},{ "bob": 34},{ "joe" : 4}])
                .orderBy([{field: 0}])
                .select();

这里是OrderedMap的一个实现。使用函数get()set()提取键值对或将键值对推送到OrderedMap。它在内部使用一个数组来维持秩序。

class OrderedMap {
  constructor() {
    this.arr = [];
    return this;
  }
  get(key) {
    for(let i=0;i<this.arr.length;i++) {
      if(this.arr[i].key === key) {
        return this.arr[i].value;
      }
    }
    return undefined;
  }
  set(key, value) {
    for(let i=0;i<this.arr.length;i++) {
      if(this.arr[i].key === key) {
        this.arr[i].value = value;
        return;
      }
    }
    this.arr.push({key, value})
  }
  values() {
    return this.arr;
  }
}
let m = new OrderedMap();
m.set('b', 60)
m.set('a', 10)
m.set('c', 20)
m.set('d', 89)
console.log(m.get('a'));
console.log(m.values());

https://github.com/js-sdsl/js-sdsl

Js-sdsl中的OrderedMap可能会有所帮助。

这是一个排序映射,实现了对C++STL映射的引用。

/*
 * key value
 * 1   1
 * 2   2
 * 3   3
 * Sorted by key.
 */
const mp = new OrderedMap(
    [1, 2, 3].map((element, index) => [index, element])
);
mp.setElement(1, 2);        // O(logn)
mp.eraseElementByKey(1)     // O(logn)
// custom comparison function
mp = new OrderedMap(
    [1, 2, 3].map((element, index) => [index, element]),
    (x, y) => x - y
);
// enable tree iterator index (enableIndex = true)
console.log(new OrderedMap([[0, 1], [1, 1]], undefined, true).begin(),next().index);   // 1