-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.umd.js
162 lines (145 loc) · 5.53 KB
/
index.umd.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.Closest = factory());
}(this, function () { 'use strict';
/**
* Return closest Number/Vector2/Vector3/VectorN from a given list
* uses caching for faster response. Has the ability to return every match
* only once.
*/
class Closest {
/**
* Creates an instance of Closest.
* @param {Array} list Elements of reference can be an Array of Numbers
* or Array's with an equal length
* @param {Boolean} unique If set to true, every entry from `list`
* can be returned only once
* unit clearCache() is called
*/
constructor(list, unique) {
// creates a copy of list
this.list = Array.from(list);
this.dimensions = Closest.getDimensions(list[0]);
this.unique = unique;
// sets the adequate diff method based on the depth of the vectors
this.diff = this.dimensions > 1 ? Closest.nDimensionalDiff(this.dimensions)
: Closest.oneDimensionalDiff;
// console.log(this.diff)
// inits the cache and previouslyReturnedIndexes properties
this.clearCache(false);
}
/**
* determines if the items in the list are simple numbers or arrays
* @static
* @param {Number|Array} item
* @return {Number} number of dimensions (1 being a simple number,
* everything above is an array)
*/
static getDimensions(item) {
return typeof item == 'number' ? 1 : item.length;
}
/**
* diff function for simple numbers
* @static
* @param {Number} val1
* @param {Number} val2
* @return {Number} Abstract difference between two numbers
*/
static oneDimensionalDiff(val1, val2) {
return Math.abs(val1-val2);
}
/**
* diff function for array's of N numbers
* @static
* @param {Number} dimensions number of dimensions of your vector
* @return {Function} returns the adequate diff function
* Euclidean distance
* (https://en.wikipedia.org/wiki/Euclidean_distance)
*/
static nDimensionalDiff(dimensions) {
if (dimensions == 2) {
return (val1, val2) => (
(val1[0] - val2[0]) * (val1[0] - val2[0]) +
(val1[1] - val2[1]) * (val1[1] - val2[1])
);
} else if (dimensions == 3) {
return (val1, val2) => (
(val1[0] - val2[0]) * (val1[0] - val2[0]) +
(val1[1] - val2[1]) * (val1[1] - val2[1]) +
(val1[2] - val2[2]) * (val1[2] - val2[2])
);
} else {
// elegant but slow solution
return (val1, val2) => (
val1.reduce(
(acc, val, i) => ((val - val2[i]) * (val - val2[i]) + acc)
, 0)
);
}
}
/**
Public method to rest cached value / result paris
* especially if set to unique (return every match only once)
* you may want to reset the previously returned indexes
* @param {Boolean} indexOnly if you are using "unique" mode only the returned
* indexes are cleared by default
*/
clearCache(indexOnly = this.unique) {
if (!indexOnly) {
this.cache = {};
}
this.previouslyReturnedIndexes = [];
}
/**
* gets the closes Number/VectorN
* @param {Number|Array} val reference number or array
* @return {Object|Null} closes match within lists containing
* {
* closest: {Number|Array} closest entry from list
* index: {Number} index within list
* distance: {Number} Distance within the
* coordinate system
* }
*/
get(val) {
let minDistance = Infinity;
let index = 0;
let closest = this.list[index];
// is there a better way to do this? If "val" is only a number, it seams
// like a big overhead to JSON stringify it every-time, I don't see an other
// way when val is an array thought. Other than something like
// cache[val[0]][val[1]][val[3]] or whatever
const valUID = JSON.stringify(val);
// returns previously found match
if (!this.unique && this.cache.hasOwnProperty(valUID)) {
return this.cache[valUID];
}
// if set to return every value in the list only once
// and being out of entries in the list
if (
this.unique && this.previouslyReturnedIndexes.length === this.list.length
) {
return null;
}
for (let i = 0; i < this.list.length; i++) {
// skip if set to unique and value was returned previously
if (!(this.unique && this.previouslyReturnedIndexes.indexOf(i) > -1) ) {
const distance = this.diff(val, this.list[i]);
if (distance < minDistance) {
minDistance = distance;
index = i;
closest = this.list[i];
}
}
}
// save previously returned indexes if set to unique mode,
if (this.unique) {
this.previouslyReturnedIndexes.push(index);
}
// return and save in cache
return this.cache[valUID] = {closest, index};
}
}
return Closest;
}));