JavaScript:创建一个将多个键Map到一个值的Map?

t2a7ltrp  于 2022-12-25  发布在  Java
关注(0)|答案(4)|浏览(227)

我有一个键和一个值的列表。例如:

keys = ["keyA", "keyB", "keyC"];

value = 100;

我正在尝试创建一个函数来创建一个Map,以便:

map["keyA"]["keyB"]["keyC"] = 100;

根据给出的答案here,我认为这是最佳的数据结构:
无论如何,我面临的挑战是我需要一个函数来为任意数量的键创建Map,我尝试过在循环中这样做,但无法让它工作,因为我不知道如何访问不同级别的Map,而且感觉很草率:

for(var i=0; i<keys.length; i++){
    for(var j=0; j<i; j++){
        maps[keys[0]]...[keys[j]] = {};
        if(j+1 === i){
            maps[keys[0]]...[keys[j]][keys[i]] = value;
        }
    }
}

如何创建Map?

kx5bkwkv

kx5bkwkv1#

你可以试着存储一个对最后创建的内部对象的引用,并在循环中更深入,以便使它在线性时间内:

// Input data:
var keys = ["keyA", "keyB", "keyC", "keyD", "keyE"];
var value = 100;

// Algorithm:
var result = {};
var last = result;

for (var i = 0; i < keys.length - 1; i++)
{
  last = (last[keys[i]] = {});

  // can be change to a two-liner:
  // last[keys[i]] = {};
  // last = last[keys[i]];
}
last[keys[keys.length - 1]] = value;

// Output:
document.body.innerHTML = JSON.stringify(result);
document.body.innerHTML += "<br/><br/>" + result["keyA"]["keyB"]["keyC"]["keyD"]["keyE"];
sqxo8psd

sqxo8psd2#

如果您不想维护对象的层次结构,我建议您连接键,并将值与连接的字符串一起存储为键。
这里假设你总是有相同的keys数组,如果keys数组是外部提供的,你可以在连接之前排序。
请参阅片段。

var keys = ["keyA", "keyB", "keyC", "keyD", "keyE"];
var value = 568;

var datastructure = {};

datastructure[keys.join("-")] = value;

document.getElementById("output").innerHTML = datastructure[keys.join("-")];
<span id="output"></span>
j0pj023g

j0pj023g3#

假设这个结构是一个嵌套深度任意的树,首先你可以从一个帮助函数中获益,这个帮助函数允许你安全地访问可能不存在的路径:

function path(obj, str) {
  return str.split('.').reduce(function (acc, key) {
    return acc instanceof Object ? acc[key] : undefined;
  }, obj);
}

您还需要一种方法来整齐地设置这些路径:

function setPath(obj, str, val) {
  var path = str.split('.');
  var key  = path.pop();

  var target = path.reduce(function(acc, key) {
    return acc[key] = acc[key] instanceof Object ? acc[key] : {};
  }, obj);

  target[key] = val;
}

然后,您就有了一个干净的接口来存储和检索这些数据。

map = {};

setPath(map, 'keyA.keyB.keyC', 100);
path(map, 'keyA.keyB.keyC') // 100;
path(map, 'keyA.keyX.keyY') // undefined;

如果您愿意,可以让它接受键数组,而不是点标记路径,如下所示(只需省略拆分步骤)。
请注意,如果您对访问树中除叶子以外的节点不感兴趣,或者希望能够同时拥有map.a.b * 和 * map.a的值,则可以通过使用单一深度来更简单地实现这一点:

map[keys.join('.')] = 100;

既然你已经在注解中添加了一个目标,即这里的目标实际上只是将一个值与一组键相关联,而根本没有实际的树结构:

function get(map, keys) {
  var key = keys.sort().join('.');
  return map[key];
}

function set(map, keys, val) {
  var key = keys.sort().join('.');
  map[key] = val;
}

如果密钥中似乎有句点字符,请替换为可以安全保留的其他字符。

0mkxixxg

0mkxixxg4#

编辑2022年12月24日

我已经创建了一个ES模块的顺序不可知的多Map。我将在这里解释你如何可以设置它的OP的用例。
https://github.com/martian17/ds-js
首先,您需要将存储库克隆到项目中,或者复制代码。

$ git clone https://github.com/martian17/ds-js.git

下面是一个使用情形示例

// import it to your project
import {OrderAgnosticMultiMap} from "path_to_ds-js/multimap.mjs";

// Instantiate
const map = new OrderAgnosticMultiMap();

// Register values
map.set("keyA", "keyB", "keyC", "content 1");
map.set("keyA", "keyC", "keyC", "content 2");
map.set("keyA", "keyB", "keyB", "content 3");
// The keys can be any object
map.set(map, OrderAgnosticMultiMap, map, window, document, "content 4");

// Get values (keys can be in different orders)
console.log(map.get("keyB", "keyC", "keyA"));
// log: "content 1"
console.log(map.get("keyB", "keyB", "keyC"));
// log: undefined
map.set(document, map, window, OrderAgnosticMultiMap, map);
// log: "content 4"

// Check if a value exists for some keys
console.log(map.has("keyC", "keyC", "keyA"));
// log: true
console.log(map.has("keyA", "keyC", "keyA"));
// log: false

// Loop through values
for(let [tally,value] of map){
    console.log(tally,value);
}
// log:
// Map(3) {"keyA" => 1, "keyB" => 1, "keyC" => 1} 'content 1'
// Map(3) {"keyA" => 1, "keyC" => 2} 'content 2'
// Map(3) {"keyA" => 1, "keyB" => 2} 'content 3'
// Map(3) {map => 2, OrderAgnosticMultiMap => 1, window => 1, document => 1} 'content 4'

// Delete keys
map.delete("keyC", "keyB", "keyA");
map.delete("keyB", "keyB", "keyA");
map.delete("keyC", "keyC", "keyA");
console.log(map.has("keyC", "keyC", "keyA"));
// log: false

编辑前

如果有任何人想知道是否有一个解决方案的多键ES6Map,这里是我采取。
但是顺序很重要,所以map.get(a,B,c)和map.get(c,a,b)将获取不同的值。
当然,您可以将其用作字符串到对象的Map,因此它也满足OP的用例。

class MultiMap{
    map = new Map;
    own = Symbol();// unique value that doesn't collide
    set(){
        let lst = [...arguments];
        let val = lst.pop();
        let map = this.map;
        for(let k of lst){
            if(!map.has(k))map.set(k,new Map);
            map = map.get(k);
        }
        map.set(this.own,val);// to avoid collision between the same level
        return val;
    }
    get(...lst){
        let map = this.map;
        for(let k of lst){
            if(!map.has(k))return undefined;
            map = map.get(k);
        }
        return map.get(this.own);
    }
    has(...lst){
        let map = this.map;
        for(let k of lst){
            if(!map.has(k))return false;
            map = map.get(k);
        }
        return map.has(this.own);
    }
    delete(...lst){
        let map = this.map;
        let maps = [[null,map]];
        for(let k of lst){
            if(!map.has(k))return false;
            map = map.get(k);
            maps.push([k,map]);
        }
        let ret = map.delete(this.own);
        for(let i = maps.length-1; i > 0; i--){
            if(maps[i][1].size === 0){
                maps[i-1][1].delete(maps[i][0]);
            }else{
                break;
            }
        }
        return ret;
    }
}

用例示例

let a = {a:"a"};
let b = {b:"b"};
let c = {c:"c"};

let mm = new MultiMap;

//basic operations
console.log(mm.set(a,b,c,"abc"));// "abc"
console.log(mm.get(a,b,c));// "abc"
console.log(mm.has(a,b,c));// true
console.log(mm.delete(a,b,c));// true

// overlapping keys can be handled fine as well
mm.set(a,b,"ab");
mm.set(a,"a");
console.log(mm.get(a,b));// "ab"
console.log(mm.get(a));// "a"

对于任何对我的用例感兴趣的人:我试图做一个事件监听器 Package 器,它可以在内部Map到多个事件(mousedown =〉mousedown,touchstart等)。我需要在调用.on()时缓存参数,以便.off()可以找到正确的事件监听器集来删除。

相关问题