获取对象中的下一个键值对

Posted

技术标签:

【中文标题】获取对象中的下一个键值对【英文标题】:Get next key-value pair in an object 【发布时间】:2012-09-12 09:42:38 【问题描述】:

给定一个键,我想在一个对象中找到下一个属性。我不能依赖要排序或顺序的键(它们是 uuid)。请参阅下面的我想要的简单示例:

var db = 
  a: 1,
  b: 2,
  c: 3


var next = function(db, key) 
  // ???


next(db, 'a');  // I want 2
next(db, 'b');  // I want 3

我也想要一个 prev() 函数,但我相信这将是相同的解决方案。

这似乎是一个微不足道的问题,但我一生都无法弄清楚如何去做。

很高兴使用 underscore.js 或用咖啡脚本编写的解决方案 :)

【问题讨论】:

“所以,给定一个“键”,我想在一个对象中寻找它的第一个实例。” - 你的意思是“它的第一个实例” “?您不能拥有多个具有相同名称的属性,因此不可能有第二个实例。 “我想返回 NEXT 属性的值。” - 您不能依赖对象属性按任何特定顺序排列(尽管有些 - 大多数? - 浏览器倾向于按顺序返回属性它们被创建)。如果您需要订购它们,则需要使用数组。 @nnnnnn 这是关于浏览器属性排序的好点谢谢。我想我可能只需要将这些信息拉入一个有序数组然后遍历它。 我认为这不是一个好问题。因为dbobject'key的顺序不一致。这个问题的根源在于数据结构——db。如果db无法更改,则没有答案。 尽管它已经一岁了,我不同意你@island205。这个问题是我要问的,尽管我有预感它无法完成。接受的答案也给了我一个解决方法;虽然过程有点复杂,但它是一个有效的解决方案。 【参考方案1】:

ts/es6 版本。我只是从 storeObject 中获取键,寻找下一个索引。

 let keys = Object.keys(storeObject);
 let nextIndex = keys.indexOf(theCurrentItem) +1;
 let nextItem = keys[nextIndex];

【讨论】:

Object.keys 有很好的支持。目前,唯一不兼容的主要浏览器版本是 IE 6-8。 优秀的答案!【参考方案2】:

正确答案是:你不能那样做,就像objects are unordered as per ECMAScript's spec。

我建议您使用有序结构(如数组)来解决问题:

var db = [
  key: 'a', value: 1,
  key: 'b', value: 2,
  key: 'c', value: 3
];

那么next 函数可以是这样的:

var next = function(db, key) 
  for (var i = 0; i < db.length; i++) 
    if (db[i].key === key) 
      return db[i + 1] && db[i + 1].value;
    
  
;

如果keydb 上不存在或者它是最后一个,next 返回undefined。如果您永远不会要求最后一项的下一项,您可以通过删除三元 &amp;&amp; 运算符并直接返回 db[i + 1].value 来简化该函数。

您还可以使用一些 Underscore.js 实用方法使 next 更简单:

var next = function(db, key) 
  var i = _.pluck(db, 'key').indexOf(key);
  return i !== -1 && db[i + 1] && db[i + 1].value;
;

(在这种情况下,next 有时会返回 false...但它仍然是一个虚假值 :))


现在,一个更务实的答案可能是,因为大多数浏览器在迭代对象时都会尊重对象的初始化顺序,因此您可以按照其他答案的建议使用 for in 循环对其进行迭代。我建议使用Object.keys 来简化遍历数组的工作:

// Assuming that db is an object as defined in the question.
var next = function(db, key) 
  var keys = Object.keys(db)
    , i = keys.indexOf(key);
  return i !== -1 && keys[i + 1] && db[keys[i + 1]];
;

【讨论】:

@just2n 很公平,这些都是有效点。关于性能开销,我认为我们没有太多关于输入大小或这是否是性能关键操作的信息。在不知道如何使用它,甚至不知道如何构造输入的情况下,我发现很难声称这个解决方案与其他解决方案相比的性能开销会很明显。关于 Object.keys 的顺序,是的,它是未定义的,这就是我在“实用”解决方案中提到它的原因,这不是很正确。 [...] [...] 我很欣赏批评,但我认为你的评论过于消极。没有迹象表明该答案仅基于声誉而被接受;也许 OP 认为代码更易于理解/维护,或者他们只是更喜欢将所有内容存储到普通数组中的方法。【参考方案3】:
function next(db, key)   
  var found = 0; 
  for(var k in db)
    if(found) return db[k]; 
    if(k == key) found = 1; 
  

【讨论】:

【参考方案4】:

对此的直接解决方案是将数据存储在一个数组中,并使用对象来简单地将索引存储在对象所在的数组中。

var db = 
    data: [1, 2, 3],
    index: 
        a: 0,
        b: 1,
        c: 2
    
;
function next(db, key) 
    var next = db.index[key] + 1;
    if (next >= db.data.length) 
        return null;
    
    return db.data[next];

function prev(db, key) 
    var next = db.index[key] - 1;
    if (next < 0) 
        return null;
    
    return db.data[next];

function add(db, key, value) 
    db.index[key] = db.data.push(value) - 1;

function remove(db, key) 
    var index = db.index[key], x, temp;
    if (index !== undefined) 
        delete db.index[key];
        db.data.splice(index, 1);
        // Update indices of any elements after the removed element
        for (x in db.index) 
            temp = db.index[x];
            if (temp > index) 
                db.index[x] = temp - 1;
            
        
    

基本思想是使用有序结构(在本例中为数组)以顺序方式保存数据。在这种情况下,next和prev都是常数时间,add是摊销常数时间,delete是O(N)。

ECMA 标准不保证键的顺序,因此 for/in 不需要按照添加键的顺序(尽管在实践中,这往往是常见的实现)。在这个解决方案中,我使用一个数组来显式跟踪插入顺序。

编辑:我之前忽略了 splice 的删除问题。删除拼接值后,所有值的索引都会变得不正确。该修复不会影响操作的运行时间复杂度。具有更少删除的更快版本可以让数组变得稀疏,而不是拼接,只需将索引设置为 null 以释放存储在那里的任何引用。这会将删除操作降低到 O(1)。

function remove(db, key) 
    var index = db.index[key];
    if (index !== undefined) 
        delete db.index[key];
        db.data[index] = null;
    

【讨论】:

【参考方案5】:

使用 undercore.js,您可以获取对象的键并完成任务。但我不确定键值对是否以任何方式开始排序:

var next = function(db, key) 
    var keys = _.keys(db);
    var index = _.indexOf(keys, key);
    if(index+1<keys.length)
         return db[keys[index+1]];
    else
        return null;
    

jsFiddle:http://jsfiddle.net/QWhN2/

【讨论】:

indexOf 是 O(N),.keys 在每次调用时创建每个键的副本(运行时和内存空间中的 O(N)) 是的,我同意。没想到。我认为@andbeyond 的答案,或者类似的东西可以使用,因为它可以节省内存【参考方案6】:

我 2021 年来到这里,所以我会发布一个 Es6 解决方案。

一个简单的解决方案,让您在给定起始键的情况下导航对象:

const navObj = (obj, currentKey, direction) => 
    return Object.values(obj)[Object.keys(obj).indexOf(currentKey) + direction];
;

const db = 
  a: 1,
  b: 2,
  c: 3
;

console.log(navObj(db, 'a', 1));
console.log(navObj(db, 'a', 2));
console.log(navObj(db, 'b', -1));

【讨论】:

以上是关于获取对象中的下一个键值对的主要内容,如果未能解决你的问题,请参考以下文章

从 sql BigQuery 中的数组对象中获取键值对

redis笔记

怎样从对象中获取键值并保存在对象中

23.说一下 HashMap 的实现原理?

java 怎么样获取Map中第几个键值对

javascript中数组取值的问题,如何取出键值对中的值?