web-dev-qa-db-ja.com

オブジェクトの次のキーと値のペアを取得する

キーを指定して、オブジェクトの次のプロパティを検索します。キーの順序付けやシーケンシャルに依存することはできません(それらは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を使用するか、coffeescriptで記述されることを嬉しく思います:)

20
captainclam

正解は次のとおりです。 ECMAScriptの仕様に従ってオブジェクトは順序付けられていない なので、これを行うことはできません。

問題の目的のために、配列などの順序付けられた構造を使用することをお勧めします。

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に存在しないか、最後に存在した場合、nextundefinedを返します。最後のアイテムの次のアイテムを要求しない場合は、3項&&演算子を削除して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;
};

(この場合nextfalseを返すことがあります...しかし、それはまだ偽の値です:))


さて、もっと実用的な答えは、ほとんどのブラウザはオブジェクトを反復するときにオブジェクトが初期化された順序を尊重するので、他の答えが示唆するように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]];
};
19
epidemian

ts/es6バージョン。 storeObjectからキーを取得するだけで、次のインデックスを探します。

 let keys = Object.keys(storeObject);
 let nextIndex = keys.indexOf(theCurrentItem) +1;
 let nextItem = keys[nextIndex];
8
httpete
function next(db, key){   
  var found = 0; 
  for(var k in db){
    if(found){ return db[k]; }
    if(k == key){ found = 1; }
  }
}
6
anson

これに対する直接的な解決策は、データを配列に格納し、オブジェクトを使用して、オブジェクトが存在する配列にインデックスを単に格納することです。

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は、キーが追加された順序である必要はありません(実際には、一般的な実装になる傾向があります)。このソリューションでは、配列を使用して挿入順序を明示的に追跡しています。

編集:以前はスプライスで削除の問題を見落としていました。削除のために接合された値の後、すべての値のインデックスが正しくなくなります。この修正は、操作の実行時間の複雑さに影響を与えません。削除が少ない高速バージョンでは、配列がまばらになり、スプライシングの代わりにインデックスを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を使用して、オブジェクトのキーを取得し、トリックを実行できます。しかし、Key-Valueペアが次のように何らかの順序で並べられているかどうかはわかりません。

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/

3
janith