Backbone Collection:iterate&有效搜索

时间:2014-09-22 15:14:24

标签: javascript backbone.js

我拥有的东西:我正在写我非常琐碎的媒体播放器。所以在其中的某个地方我收集了字幕(假设每部电影约有2千个字幕)。所以每个字幕都如下:

Object {
  id: "1"
  startTime: 2468
  endTime: 4743
  text: "Test subtitle."
}

我想:非常有效地做两件事:

  1. .next()方法应该让我得到下一个副标题。
  2. .getByTime(neededTime)以有效的方式通过整个字幕集合进行二分搜索。关键是neededTime可能介于startTimeendTime属性之间(即3000)。
  3. 问题:我会为我的字幕使用Backbone集合,但它将如何处理这两个要求?我的意思是,手动实现Iterator模式和二进制搜索可能会更好吗?

1 个答案:

答案 0 :(得分:2)

不知道这是否是最好的解决方案,但我最终会:

if (!Number.isInteger) {
    Number.isInteger = function isInteger (nVal) {
        return typeof nVal === "number" && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal;
    };
}

/**
 * Model
 */
var Subtitle = Backbone.Model.extend({
    defaults: {
        'startTime': '',
        'endTime': '',
        'text': ''
    },

    validate: function(attrs) {
        if (!this.get('startTime')) return 'You missed startTime';
        if (!this.get('endTime')) return 'You missed endTime';
        if (!this.get('text')) return 'You missed text';
        if (this.get('startTime') > this.get('endTime')) return 'startTime cannot be greater than endTime';
    },

    /**
     * Compare two time periods
     * @param that Subtitle with which we compare the current one
     * @returns {number} positive if {@code this} is greater than {@code that}, negative if {@code this} is less than {@code that}
     * and zero if they are equals or overlaps or one time period contains another.
     */
    equals: function(that) {
        if (!(that instanceof Subtitle)) throw Error('Cannot compare with element that does not belong to Subtitle type');
        if (this.get('startTime') > that.get('endTime')) return 1;
        else if (that.get('startTime') > this.get('endTime')) return -1;
        else return 0;
    },

    /**
     *
     * @param moment moment for which we need to find a time period
     * @returns {number} positive if {@code moment} belongs to time period greater than {@code this},
     * negative if {@code moment} belongs to time period less than {@code this}
     * and zero if the curect time period contain the {@code moment}.
     */
    containMoment: function(moment) {
        if (!Number.isInteger(moment)) throw Error('Moment should be an Integer');

        if (moment >= this.get('startTime') && moment <= this.get('endTime')) return 0;
        else if (moment < this.get('startTime')) return -1;
        else if (moment > this.get('endTime')) return +1;
        else throw Error('Impossible case :-)');
    }
});

/**
 * Collection
 */
var Subtitles = Backbone.Collection.extend({
    model: Subtitle,
    index: 0,

    next: function() {
        return this.at(this.index++);
    },

    getByMoment: function(moment) {
        return this._binarySearch(this.toArray(), moment);
    },

    _binarySearch: function(array, moment) {
        'use strict';

        var minIndex = 0;
        var maxIndex = array.length - 1;
        var currentIndex;
        var currentElement;

        while (minIndex <= maxIndex) {
            currentIndex = (minIndex + maxIndex) / 2 | 0;
            currentElement = array[currentIndex];

            if (currentElement.containMoment(moment) > 0) {
                minIndex = currentIndex + 1;
            }
            else if (currentElement.containMoment(moment) < 0) {
                maxIndex = currentIndex - 1;
            }
            else {
                return array[currentIndex];
            }
        }

        throw Error('No subtitle for this moment');
    }
});

/**
 * Test
 */
var data = [ 
  { id: '1',
    startTime: 2468,
    endTime: 4743,
    text: 'Here\'s little Ben nodding off.' },
  { id: '2',
    startTime: 5389,
    endTime: 7698,
    text: 'Look at Aunt Monica\'s Little boy.' },
  { id: '3',
    startTime: 7948,
    endTime: 11099,
    text: '-Look, he\'s got Ross\' hair cut.\n-Let me see.' },
  { id: '4',
    startTime: 11948,
    endTime: 14907,
    text: 'Oh, God!\nIs he just the sweetest thing?' },
  { id: '5',
    startTime: 15148,
    endTime: 17946,
    text: 'You must just want\nto kiss him all over.' }
];
var subtitles = new Subtitles();
subtitles.add(data);

var moment = 3000;
console.log(subtitles.next().attributes);
console.log(subtitles.next().attributes);
console.log(subtitles.next().attributes);
console.log(subtitles.next().attributes);
console.log(subtitles.getByMoment(moment).attributes);