二维查找表-如何在javascript中存储与头



我需要根据两个值在表中查找结果来计算结果。表是这样的:

     Bar  <10   20   30   40   50   60
    Foo
   <1.0   .14  .17  .22  .29  .31  .45
    1.1   .16  .18  .25  .32  .37  .51
    1.2   .19  .20  .29  .37  .41  .53
    1.3   .21  .22  .32  .44  .49  .59
    1.4   .25  .26  .34  .51  .52  .68
    1.5   .29  .31  .39  .53  .54  .71

,其中顶部的数字是Bar值的范围(其中给出的数字是范围的顶部),左侧的数字是Foo值的范围。如果Bar = 24, Foo=1.3,查找结果是。32。(当然,上面的数字是虚构的,真正的表格大约是25 × 25。)

所有这些都必须在javascript中完成,包括存储查找值。

一种可能的方法是将值存储为哈希的哈希:

var lookup = { 1.0: {10:.14, 20: .17, 30:.22}); etc. etc.

,其中外部值是Foo值,每个Foo值映射到一个对象,该对象将Bar值映射到答案。凌乱且难以阅读,但相当明确。

另一种方法是将值存储为数组的数组,并从其他地方获取索引。也就是说,我将值存储在顶部:

var BarColumns = [10, 20, 30, 50, 50, 60];

和下面的

var FooRows = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5];

当给定一些值时,我使用上面的列表将这些值转换为索引,然后使用这些索引在包含答案的二维数组中查找:

var lookup = [
  [.14, .17, .22, .29, .31, .45],
  [.16, .18, .25. .32, .37, .51],
  etc.
];

这与原始表更接近,但是没有头,表数据根本不是很容易读,我认为这更难维护——特别是如果列改变了。

另一个选项——超显式,在每个对象中重复范围:

var lookup = [{foo:1.0, bar:10, result:.14}, {foo:1.0, bar:20, result: .17}, etc.];

这是非常明确的,但应该是巨大的25 X 25的值。

所以,我的问题是:什么是最好的方式来存储我的原始表,这样我可以用它来查找,也有它是人类可读和可维护的?我上面的一个想法?或者完全是别的什么?

我应该补充的要点:1)实际查找不会经常发生,所以我不太担心性能(在合理范围内),2)我从客户端获得了表格数据作为Excel电子表格,但最好的结果是如果我能把最终的javascript交给客户端并让他们维护它-因此关注可读性。

jsFiddle Demo

我建议为这个过程创建一个数据结构。这将涉及一个LookUp"类",它将中介一组DataPoint对象,这些对象将包含一个范围、一个栏和一个值。 <<p> 数据结构/em>
var Range = function(lower,upper){
 this.lower = lower;
 this.upper = upper;
};
var DataPoint = function(range,bar,value){
 this.range = range;
 this.bar = bar;
 this.value = value;
};
var LookUp = function(){
 this.DataPoints = [];   
};
LookUp.prototype.add = function(data){
 this.DataPoints.push(data);  
};
LookUp.prototype.load = function(BarColumns,FooRows,ValueColumns){
 ValueColumns = ValueColumns.split(" ").filter(Boolean);
 for( var n = 0; n < FooRows.length; n++ ){
  var range = 0;
  for( var i = 0 ; i < BarColumns.length; i++ ){
   var val = parseFloat(ValueColumns[(BarColumns.length * n) + i],10);
   var point = new DataPoint(new Range(range,BarColumns[i]),FooRows[n],val);
   this.add(point);
   range = BarColumns[i];
  }
 }   
};
LookUp.prototype.find = function(x,bar){
 for(var i = 0; i < this.DataPoints.length; i++){
  var point = this.DataPoints[i];
  if( x > point.range.lower && x < point.range.upper && point.bar == bar){
   return point.value;
  }
 }
};

样本数据

注意:valCols字符串只是复制粘贴你的网格。然而,这可以很容易地从Excel中复制出来。在每一行的末尾进行串联,然后在串联列的底部,串联所有这些,它将与这里显示的valCols相同。

var BarColumns = [10, 20, 30, 50, 50, 60];
var FooRows = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5];
var valCols = ".14  .17  .22  .29  .31  .45 .16  .18  .25  .32  .37  .51 .19  .20  .29  .37  .41  .53 .21  .22  .32  .44  .49  .59 .25  .26  .34  .51  .52  .68 .29  .31  .39  .53  .54  .71";

设置

var lookup = new LookUp();
lookup.load(BarColumns,FooRows,valCols);
使用

console.log(lookup.find(24,1.3));//3.2
alert(lookup.find(24,1.3));//3.2

我更喜欢使用数组的数组来存储查找表,因此查找可以从二进制搜索中受益。

var foo = [1, 2, 3, 4, 5];
var bar = [6, 7, 8, 9, 10];
var table = [];
// populate example table
for (var i = 0; i < bar.length; i++) {
    table[i] = [];
    for (var j = 0; j < foo.length; j++) {
        table[i][j] = foo[j] * bar[i];
    }
}
console.log(table);
var binarySearch = function (arr, x) {
    var a = 0;
    var b = arr.length - 1;
    var m;
    if (x > arr[b]) throw "Search key is too big";
    while (b > a) {
        m = Math.floor((a + b) / 2);
        if (arr[m] == x) return m;
        if (arr[m] > x) {
            if (m - 1 < 0) return 0;
            if (x > arr[m - 1]) return m;
            b = m - 1;
        } else {
            if (m + 1 < arr.length && x < arr[m + 1]) return m + 1;
            a = m + 1;
        }
    }
    return a;
}
console.assert(binarySearch(foo, 0.5) == 0);
console.assert(binarySearch(foo, 1) == 0);
console.assert(binarySearch(foo, 1.5) == 1);
console.assert(binarySearch(foo, 2) == 1);
console.assert(binarySearch(foo, 2.5) == 2);
//console.log(binarySearch(foo, 6));
var lookup = function (f, b) {
    var col = binarySearch(foo, f);
    var row = binarySearch(bar, b);
    return table[row][col];
}
console.assert(lookup(3, 8) == 24);
console.assert(lookup(2.5, 8) == 24);
console.assert(lookup(2.5, 7.5) == 24);
console.assert(lookup(4, 9) == 36);

小提琴:http://jsfiddle.net/3Lw5C/2/

由于非技术人员将管理它,我们可以构建一个UI来管理foobar范围和查找表值。然后,所有的值都可以序列化,并且很容易地复制和粘贴到js代码中。

var columns = 3;
var rows = 3;
var html = '';
for (var i = 0; i < columns+1; i++) {
    html += '<tr>';
    for (var j = 0; j < rows+1; j++) {
        if (i == 0 && j == 0) {
            html += '<td></td>';
            continue;
        }
        var classname = '';        
        if (i == 0) {
            classname = 'foo';
        } else if (j == 0) {
            classname = 'bar';
        } else {
            classname = 'data';
        }
        html += '<td class="'+classname+'"><input type="text" value="0" /></td>';
    }
    html += '</tr>';
}
$('table').html(html);
var getFoo = function() {
    var foo = [];
    $('td.foo').each(function(i,td) {
        foo[i] = parseFloat($(td).children('input')[0].value);
    })
    return foo;
};
var getBar = function() {
    var bar = [];
    $('td.bar').each(function(i,td) {
        bar[i] = parseFloat($(td).children('input')[0].value);
    })
    return bar;
};
var getData = function() {
    var data = [];
    var j;
    $('td.data').each(function(i,td) {
        if (i%columns==0) {
            j = i/columns;
            data[j] = [];
        }
        data[j][i%columns] = parseFloat($(td).children('input')[0].value);
    })
    return data;
};
var config = {};
config['foo'] = getFoo();
config['bar'] = getBar();
config['data'] = getData();
$('div').text(JSON.stringify(config));

fiddle: http://jsfiddle.net/MLKsf/

散列对于查找相同的值很好,但是对于表值之间的值,二维数组应该更好。

如果Foo是精确值而Bar不是,我将使用一维数组的哈希。

对于查找范围值和标头,每个值使用一维数组

您真的需要底层数据是人类可读的吗?或者您只是需要一种对开发人员友好的方式来访问给定的值?

如果我们抛开人类可读的问题,这看起来像是一个相当标准的内存vs性能权衡。你可以使用一个紧凑的表示(二维数组),然后提供一个用户友好的函数,比如lookup(foo, bar),它做一点处理来获得值,或者你可以用一个更详细的格式存储数据(哈希的哈希),并允许直接lookup[foo][bar]访问。请注意,2d数组也是JS中哈希的哈希值,因此可能很少或没有内存差异。我将放弃超显式的想法,因为它的查找时间可能比其他两种都要慢得多,因为在最坏的情况下,您需要遍历整个列表以找到正确的值。

这里有一些进一步的考虑:

  • 底层数据的可读性到底有多重要?哈希的哈希在这里可能有一个小的优势,但只是一个小的优势(例如,它不太适合console.table()进行调试)。

  • 数据总是密集的,或者你可能有稀疏的表,其中二维数组将有一堆null ?在这种情况下,哈希的哈希可能是一种更紧凑和可用的表示。

  • 数据是如何到达你的,哪些格式需要更多的预处理?如果你真的把它直接放在代码中,那么任何一种格式都可以通过明智的选项卡变得相对易读。

  • 你几乎肯定想在任何情况下提供一个lookup()函数,这样你就不会硬编码的实现作为一个2d数组到处并锁定自己。

  • 如何容易得到正确的范围位置为给定的输入值?这将取决于你是否知道范围是均匀间隔的,总是一个给定的小数位数等,在这种情况下,你可以用数学来确定哈希键;如果范围刻度是任意的,那么"头数组"方法有优点,因为您可能需要遍历头以找到正确的键。

相关内容

  • 没有找到相关文章

最新更新