簡體   English   中英

如何移動到數組的上一個/下一個元素

[英]How to move to prev/next element of an array

假設我們有一個整數列表:

var fibonacci = [1,1,2,3,5,8,13,21];

我希望能夠以下列方式獲取下一個和上一個元素(只是移動元素指針,而不修改數組)(例如,可能 go 沒有原型來重新定義 Array 接口,但為什么不這樣做):

fibonacci.prev(); // returns false
fibonacci.next(); // returns 1
fibonacci.next(); // returns 1
fibonacci.next(); // returns 2
fibonacci.next(); // returns 3
fibonacci.next(); // returns 5
fibonacci.next(); // returns 8

fibonacci.prev(); // returns 5

fibonacci.next(); // returns 8
fibonacci.next(); // returns 13
fibonacci.next(); // returns false

如果要將列表保留為Array ,則必須更改其[[prototype]]以使其看起來像可迭代的集合:

Array.prototype.next = function() {
    return this[++this.current];
};
Array.prototype.prev = function() {
    return this[--this.current];
};
Array.prototype.current = 0;

現在每個Array都有方法prevnext ,以及current屬性,它指向“當前”元素。 警告:可以修改current屬性,從而導致不可預測的結果。

Post scriptum:當索引超出范圍時,我不建議使prevnext返回false 如果你真的想,你可以將方法更改為:

Array.prototype.next = function() {
    if (!((this.current + 1) in this)) return false;
    return this[++this.current];
};

2016年中期更新

我正在更新這個答案,因為它似乎仍在接收觀點和投票。 我應該澄清一下,給出的答案是一個概念證明,一般來說, 擴展本機類的原型是一種不好的做法 ,在生產項目中應該避免。

特別是,它並不多,因為它會for...in周期中混亂for...in對於數組應該總是避免這種情況,這對於迭代它們的元素來說絕對是一個壞習慣 - 而且因為IE9我們可以可靠地做到這一點:

Object.defineProperty(Array.prototype, "next", {
    value: function() { return this[++this.current]; },
    enumerable: false
});

主要問題是擴展本機類不是面向未來的 ,即ECMA可能會為數組引入next可能與您的實現不兼容的方法。 它已經發生了,即使是非常常見的JS框架 - 最后一種情況是MooTools' contains數組擴展 ,導致ECMA將名稱更改為includes (壞移動,IMO,因為我們已經contains在像Element.classList這樣的DOMTokenList對象中)。

話雖這么說,這並不是說你不能延長本機的原型,但你應該知道你在做什么。 我可以給你的第一個建議是選擇不會與未來標准擴展沖突的名稱,例如myCompanyNext而不是next 這將花費你一些優雅的代碼,但會讓你睡得很香。

更好的是,在這種情況下,您可以有效地擴展Array類:

function MyTraversableArray() {
    if (typeof arguments[0] === "number")
        this.length = arguments[0];
    else this.push.apply(this, arguments);

    this.current = 0;
}
MyTraversableArray.prototype = [];
MyTraversableArray.prototype.constructor = MyTraversableArray;
MyTraversableArray.prototype.next = function() {
    return this[++this.current];
};
MyTraversableArray.prototype.prev = function() {
    return this[--this.current];
};

此外,在ES6中,擴展本機類更容易:

class MyTraversableArray extends Array {
    next() {
        return this[++this.current];
    }
}

唉,轉發器在本機類擴展方面很難 ,Babel取消了它的支持。 但這是因為他們不能完全復制一些對我們的情況沒有影響的行為,所以你可以堅持使用上面的舊ES3代碼。

我通常建議不要向Array.prototype添加內容,因為那里有非常糟糕的JavaScript。 例如,如果您設置Array.protoype.next = function () {}並且有人擁有以下代碼,那么就會出現問題:

var total = 0, i, myArr = [0,1,2];
for(i in myArr) {
    total += myArr[i];
}
total; //is probably "3next"

這種對for-in循環的錯誤使用令人不安地普遍存在。 所以你要通過添加Array的原型來解決問題。 但是,構建一個包裝器來完成你想做的事情非常容易:

var iterifyArr = function (arr) {
    var cur = 0;
    arr.next = (function () { return (++cur >= this.length) ? false : this[cur]; });
    arr.prev = (function () { return (--cur < 0) ? false : this[cur]; });
    return arr;
};

var fibonacci = [1, 1, 2, 3, 5, 8, 13];
iterifyArr(fibonacci);

fibonacci.prev(); // returns false
fibonacci.next(); // returns 1
fibonacci.next(); // returns 1
fibonacci.next(); // returns 2
fibonacci.next(); // returns 3
fibonacci.next(); // returns 5
fibonacci.next(); // returns 8
fibonacci.prev(); // returns 5
fibonacci.next(); // returns 8
fibonacci.next(); // returns 13
fibonacci.next(); // returns false

幾個筆記:

首先,如果你走到最后,你可能希望它返回undefined而不是false 其次,因為此方法使用閉包隱藏cur ,所以您無法在陣列上訪問它。 所以你可能想要一個cur()方法來獲取當前值:

//Inside the iterifyArr function:
    //...
    arr.cur = (function () { return this[cur]; });
    //...

最后,你的要求還不清楚“指針”在結束的過程中保持多久。 以下面的代碼為例(假設fibonacci如上所述):

fibonacci.prev(); //false
fibonacci.prev(); //false
fibonacci.next(); //Should this be false or 1?

在我的代碼中,它將是false ,但您可能希望它為1 ,在這種情況下,您必須對我的代碼進行一些簡單的更改。

哦,因為函數返回arr ,你可以在定義它的同一行“iterify”一個數組,如下所示:

var fibonacci = iterifyArr([1, 1, 2, 3, 5, 8, 13]);

這可能會讓事情變得更清潔。 您也可以通過在陣列上重新調用iterifyArr來重置迭代器,或者您可以編寫一個方法來非常輕松地重置它(只需將cur設置為0)。

它的下一個方面現在內置於數組中,因為從ES2015開始,數組是可迭代的 ,這意味着你可以為它們獲得一個迭代器,它有一個next方法(但是繼續閱讀“prev”部分):

 const a = [1, 2, 3, 4, 5]; const iter = a[Symbol.iterator](); let result; while (!(result = iter.next()).done) { console.log(result.value); } 

迭代器只是前進,而不是兩種方式。 當然,您通常不會顯式使用迭代器,通常將它用作某些迭代構造的一部分,例如for-of

 const a = [1, 2, 3, 4, 5]; for (const value of a) { console.log(value); } 

你可以很容易地給自己一個雙向迭代器:

  1. 通過創建一個接受數組並返回迭代器的獨立函數,或者

  2. 通過子類化Array並覆蓋子類中的迭代器,或者

  3. 通過使用您自己的默認Array迭代器替換默認Array迭代器(只需確保它在前進時與默認Array完全相同!)

這是一個子類的例子:

 class MyArray extends Array { // Define the iterator function for this class [Symbol.iterator]() { // `index` points at the next value we'll return let index = 0; // Return the iterator return { // `next` returns the next next: () => { const done = index >= this.length; const value = done ? undefined : this[index++]; return { value, done }; }, // `prev` returns the previous prev: () => { const done = index == 0; const value = done ? undefined : this[--index]; return { value, done }; } }; } } // Demonstrate usage: const a = new MyArray("a", "b"); const i = a[Symbol.iterator](); console.log("next", JSON.stringify(i.next())); console.log("next", JSON.stringify(i.next())); console.log("next", JSON.stringify(i.next())); console.log("prev", JSON.stringify(i.prev())); console.log("prev", JSON.stringify(i.prev())); console.log("prev", JSON.stringify(i.prev())); console.log("next", JSON.stringify(i.next())); 
 .as-console-wrapper { max-height: 100% !important; } 

ES6為我們提供了生成器功能,可以讓我們打印出一個非常簡單的數組,如下所示

function* data() {
  yield* [1, 1, 2, 3, 5, 8, 13, 21];
}

var fibonnacci = data();

fibonnacci.next()
> {value: 1, done: false}

fibonnacci.next()
> {value: 1, done: false}

fibonnacci.next()
> {value: 2, done: false}

fibonnacci.next()
> {value: 3, done: false}

fibonnacci.next()
> {value: 5, done: false}

fibonnacci.next()
> {value: 8, done: false}

fibonnacci.next()
> {value: 13, done: false}

fibonnacci.next()
> {value: 21, done: false}

fibonnacci.next()
> {value: undefined, done: true}

但是, MDN文檔中確實存在一個示例程序,它可以幫助將fibonnacci系列打印到我們喜歡的元素上。

function* fibonacci() {
  var fn1 = 0;
  var fn2 = 1;
  while (true) {  
    var current = fn1;
    fn1 = fn2;
    fn2 = current + fn1;
    var reset = yield current;
    if (reset) {
        fn1 = 0;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value);     // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2

比修改本機對象的原型更安全的是為其他所需的數組函數創建工廠函數:

const moreArrayFunctions = arr => ({
    current: 0,
    arr,
    next(){

        if( this.current >= ( this.arr.length - 1 ) ){
             this.current = this.arr.length - 1;
        }else{
            this.current++;
        }

        return this.arr[this.current];
    },
    prev(){

        if( this.current <= 0 ){
            this.current = 0;
        }else{
            this.current--;
        }

        return this.arr[this.current];
    }
});

const fibonacci = moreArrayFunctions([1,1,2,3,5,8,13,21]);

fibonacci.next();
fibonacci.prev();
fibonacci.current

是的,您可以使用數組迭代器。

var fibonacci = [1,1,2,3,5,8,13,21];

const fibIterator = fibonacci[Symbol.iterator]()

fibIterator.next(); // {value: 1, done: false}
fibIterator.next(); // {value: 1, done: false}
fibIterator.next(); // {value: 3, done: false}
....
....
fibIterator.next(); // {value: 21, done: false}
fibIterator.next(); // {value: undefined, done: true}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM