[英]In an array, how do I find the closest key given a float value?
I'm making an "acceleration" array like this: 我正在做这样的“加速度”数组:
acc["0100"] = 1;
acc["0300"] = 2;
acc["0600"] = 4;
acc["0900"] = 8;
acc["2000"] = 16;
acc["5000"] = 32;
And, when the user presses a key, I start a timer: this._startTick = (new Date()).getTime();
而且,当用户按下一个键时,我启动一个计时器:
this._startTick = (new Date()).getTime();
Now I have a timer that checks if the key is still pressed. 现在,我有一个计时器,用于检查是否仍然按下该键。 If so, then I do something like:
如果是这样,那么我将执行以下操作:
this._delay = (new Date()).getTime() - this._startTick;
And now, based on this._delay
, I'd like to find one of the previous values (1, 2, 4 or 8). 现在,基于
this._delay
,我想找到以前的值之一(1、2、4或8)。 How would you do that? 你会怎么做?
NB: if the value is greater than " 5.0
" then the result should always be 32
. 注意:如果该值大于“
5.0
”,则结果应始终为32
。
NOTA: my goal is, given an elapsed time, find out which value is the best. 注意:我的目标是,经过一段时间后,找出哪个值是最好的。 I started the way I've just explained, but if you have another solution, I'll take it!
我以我刚刚解释的方式开始,但是如果您有其他解决方案,我会接受!
Here is the jsfiddle test page. 这是jsfiddle测试页。
var getAccForDelay = (function () {
var acc = {
0.1: 1,
0.3: 2,
0.6: 4,
0.9: 8,
2.0: 16,
5.0: 32
};
return function(delay) {
var key,
bestKey = undefined,
absDiff,
absDiffMin = Number.MAX_VALUE;
for (key in acc) {
if (acc.hasOwnProperty(key)) {
absDiff = Math.abs(delay - key);
if (absDiffMin > absDiff) {
absDiffMin = absDiff;
bestKey = key;
}
}
}
return bestKey === undefined ? undefined : acc[bestKey];
};
}());
Test: 测试:
console.clear();
console.log(getAccForDelay(0));
console.log(getAccForDelay(0.33));
console.log(getAccForDelay(3.14));
console.log(getAccForDelay(123456.789));
Output: 输出:
1
2
16
32
=== UPDATE === ===更新===
The above solution doesn't utilize of the fact that acc
is sorted by key. 上面的解决方案没有利用
acc
按键排序的事实。 I optimized the code by replacing linear search with binary search , which is much faster on long arrays. 我通过用二进制搜索替换线性搜索来优化代码,这在长数组上要快得多。 Here is the test page.
这是测试页。
var getAccForDelay = (function () {
var accKey = [ 0.1, 0.3, 0.6, 0.9, 2.0, 5.0 ],
accValue = [ 1, 2, 4, 8, 16, 32 ],
accLength = accKey.length;
return function(delay) {
var iLeft, iMiddle, iRight;
iLeft = 0;
if (delay <= accKey[iLeft])
return accValue[iLeft];
iRight = accLength - 1;
if (accKey[iRight] <= delay)
return accValue[iRight];
while (true) {
if (iRight - iLeft === 1)
return delay - accKey[iLeft] < accKey[iRight] - delay ? accValue[iLeft] : accValue[iRight];
iMiddle = ~~((iLeft + iRight) / 2);
if (delay < accKey[iMiddle])
iRight = iMiddle;
else if (accKey[iMiddle] < delay)
iLeft = iMiddle;
else
return accValue[iMiddle];
}
};
}());
It's easier to operate on an array than on an object: 在数组上进行操作比在对象上进行操作更容易:
var accArr = [];
for (time in acc) {
accArr.push({time: time, value: acc[time]});
}
Assuming you have an array, you can do: 假设您有一个数组,则可以执行以下操作:
function getValue(delay) {
var diffs = accArr.map(function (e) { return Math.abs(e.time - delay); });
return accArr[diffs.indexOf(Math.min.apply(null, diffs))].value;
}
EDIT : 编辑 :
Well, you didn't mention that this is a performance-critical function. 好吧,您没有提到这是性能至关重要的功能。 In that case, I would recommend picking a granularity (eg
0.05
, so the multiplier for delay is 20
) and pre-calculating all values from 0
to MAX_DELAY
: 在那种情况下,我建议选择一个粒度(例如
0.05
,所以延迟的乘数是20
)并预先计算从0
到MAX_DELAY
所有值:
var multiplier = 20,
granularity = 1 / multiplier;
var delayValues = (function () {
var result = [];
for (var delay = 0; delay <= MAX_DELAY; delay += granularity) {
result.push(getValue(delay));
}
return result;
})();
During the animation, fetching the value will be a simple lookup in a relatively small table: 在动画期间,获取值将是在相对较小的表中进行的简单查找:
function getValueFast(delay) {
return (delayValues[Math.round(delay * multiplier)] ||
delayValues[delayValues.length - 1])
}
JSPerf comparison between this solution and simple if
statements shows they perform equally fast for searching around a middle value. 此解决方案与简单的
if
语句之间的JSPerf比较表明,它们在搜索中间值时性能一样快。
In my humble opinion I think the best solution to this problem is to write a function which picks the best acceleration based on the time using if
statements as follows: 以我的拙见,我认为解决此问题的最佳方法是编写一个函数,该函数使用
if
语句根据时间选择最佳加速,如下所示:
function getAcceleration(time) {
if (time < 0.20) return 1;
if (time < 0.45) return 2;
if (time < 0.75) return 4;
if (time < 1.45) return 8;
if (time < 3.50) return 16;
return 32;
}
However this is a static solution. 但是,这是一个静态解决方案。 If that's alright with you then I recommend you use this method.
如果您满意,那么我建议您使用此方法。 On the other hand if you need a dynamic solution then use this instead:
另一方面,如果您需要动态解决方案,请改用以下方法:
var getAcceleration = createAccelerationMap(0.1, 0.3, 0.6, 0.9, 2.0, 5.0);
function createAccelerationMap(previous) {
var length = arguments.length, limits = [];
for (var i = 1; i < length;) {
var current = arguments[i++];
limits.push((previous + current) / 2);
previous = current;
}
return function (time) {
var length = limits.length, acceleration = 1;
for (var i = 0; i < length;) {
if (time < limits[i++]) return acceleration;
acceleration *= 2;
}
return acceleration;
};
}
Either way you may then use getAcceleration
as follows: 无论哪种方式,您都可以如下使用
getAcceleration
:
console.log(getAcceleration(0)); // 1
console.log(getAcceleration(0.33)); // 2
console.log(getAcceleration(0.64)); // 4
console.log(getAcceleration(1.42)); // 8
console.log(getAcceleration(3.14)); // 16
console.log(getAcceleration(123456.789)); // 32
See the demo: http://jsfiddle.net/QepT7/ 观看演示: http : //jsfiddle.net/QepT7/
If the 0.1
is the number of seconds, and you want to round to 1 decimal you can do something this: 如果秒数是
0.1
,并且您想舍入到小数点后1位,则可以执行以下操作:
// 0.42332 * 10 = 4.2332
// Math.round( ) will be 4
// 4 / 10 = 0.4
acc[ (Math.round(this._delay * 10) / 10).toString() ]
var seconds = this._delay.toString().substring(0,2)
console.log(acc[seconds]);
This is a straight-forward approach of your problem: First I convert the float to a string, second I cut off everything after the third character. 这是您问题的直接解决方法:首先,我将浮点数转换为字符串,然后我将第三个字符之后的所有内容都切掉。
What units are you using? 您使用什么单位?
this._startTick = (new Date()).getTime();
// ms = ms
this._delay = (new Date()).getTime() - this._startTick;
// ms = ms - ms
So to get to "0.1"
/etc from milliseconds I'm assuming you are doing 所以要从毫秒到
"0.1"
/ etc,我假设你在做
(Math.floor(ms / 100) / 10).toString();
Why not just keep everything in ms/100
so you can use integers? 为什么不将所有内容都保留在
ms/100
以便可以使用整数?
var acc = [];
acc[ 1] = 1;
acc[ 3] = 2;
acc[ 6] = 4;
acc[ 9] = 8;
acc[20] = 16;
acc[50] = 32;
Then you can create a "nearest" lookup function like this 然后,您可以像这样创建“最近的”查找功能
function find(x) {
var i = 0;
x = x | 0; // The | 0 will cause a cast to int
if (x < 0) x = 0;
if (acc[x] !== undefined) return acc[x];
if (x > acc.length) return acc[acc.length - 1];
while (++i < acc.length) {
if (acc[x - i] !== undefined) return acc[x - i];
if (acc[x + i] !== undefined) return acc[x + i];
}
}
find(this._delay / 100);
Now examples are 现在的例子是
find(30); // 16
find(100.5); // 32
find(0); // 1
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.