New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

synchronous-autocomplete

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

synchronous-autocomplete - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0-alpha.1

29

build.js
'use strict';
var roundTo = function roundTo(v, p) {
return parseFloat(v.toFixed(p));
};
// todo: handle props like `toString`
var buildIndexes = function buildIndexes(tokenize, items) {
var originalIds = [];
var tokens = Object.create(null);
var weights = Object.create(null);
var nrOfTokens = Object.create(null);
var weights = [];
var nrOfTokens = [];
var currentId = 0;
var generateId = function generateId(oldId) {
var newId = currentId++;
originalIds[newId] = oldId;
return newId;
};
var _iteratorNormalCompletion = true;

@@ -16,2 +29,3 @@ var _didIteratorError = false;

var id = generateId(item.id);
var tokensOfItem = tokenize(item.name);

@@ -27,3 +41,3 @@ var _iteratorNormalCompletion2 = true;

if (!Array.isArray(tokens[_token])) tokens[_token] = [];
if (!tokens[_token].includes(item.id)) tokens[_token].push(item.id);
if (!tokens[_token].includes(id)) tokens[_token].push(id);
}

@@ -45,4 +59,4 @@ } catch (err) {

weights[item.id] = item.weight;
nrOfTokens[item.id] = tokensOfItem.length;
weights[id] = item.weight;
nrOfTokens[id] = tokensOfItem.length;
}

@@ -67,8 +81,9 @@ } catch (err) {

var nrOfItemsForToken = tokens[token].length;
scores[token] = nrOfItemsForToken / items.length;
var score = nrOfItemsForToken / items.length;
scores[token] = roundTo(score, 6 - Math.log10(score) | 0);
}
return { tokens: tokens, scores: scores, weights: weights, nrOfTokens: nrOfTokens };
return { tokens: tokens, scores: scores, weights: weights, nrOfTokens: nrOfTokens, originalIds: originalIds };
};
module.exports = buildIndexes;

@@ -6,3 +6,3 @@ 'use strict';

var createAutocomplete = function createAutocomplete(tokens, scores, weights, nrOfTokens, tokenize) {
var createAutocomplete = function createAutocomplete(tokens, scores, weights, nrOfTokens, originalIds, tokenize) {
var byFragment = function byFragment(fragment, completion, fuzzy) {

@@ -12,5 +12,7 @@ var results = {};

// todo: hasProp check?
if (tokens[fragment]) {
var relevance = 1 + scores[fragment] + Math.sqrt(fragment.length);
// todo: for i loop
var _iteratorNormalCompletion = true;

@@ -46,2 +48,3 @@ var _didIteratorError = false;

for (var t in tokens) {
// todo: hasProp check?
if (fragment === t) continue; // has been dealt with above

@@ -59,2 +62,3 @@

// todo: for i loop
var _iteratorNormalCompletion2 = true;

@@ -143,3 +147,8 @@ var _didIteratorError2 = false;

var score = relevance * Math.pow(weights[id], 1 / 3);
results[id] = { id: id, relevance: relevance, score: score, weight: weights[id] };
results[id] = {
id: originalIds[id],
relevance: relevance,
score: score,
weight: weights[id]
};
}

@@ -146,0 +155,0 @@ }

{
"name": "synchronous-autocomplete",
"description": "Fast, simple autocompletion.",
"version": "1.0.0",
"version": "2.0.0-alpha.1",
"main": "index.js",

@@ -6,0 +6,0 @@ "files": [

@@ -9,2 +9,3 @@ # synchronous-autocomplete

[![chat on gitter](https://badges.gitter.im/derhuerst.svg)](https://gitter.im/derhuerst)
[![support me on Patreon](https://img.shields.io/badge/support%20me-on%20patreon-fa7664.svg)](https://patreon.com/derhuerst)

@@ -33,3 +34,3 @@

}, {
id: 'pomegranate',
id: 'pome',
name: 'Sour Pomegranate',

@@ -45,35 +46,42 @@ weight: 5

- *token*: A word from the fully processed search query. For example, to find an item named `Hey There!`, you may process its name into the *tokens* `hey` & `there`.
- *fragment*: A word from the process search query, which may partially match a token. E.g. the *fragment* `ther` (from the search query `Hey There!`) partially matches the *token* `there`.
- *fragment*: A word from the process search query, which may partially match a token. E.g. the *fragment* `ther` (from the search query `Hey Ther`) partially matches the *token* `there`.
- *relevance*: How well an item is matched by the search query.
- *score*: A combination of an item's *weight* and *relevance*. Use it to sort search results.
In order to be as fast an disk-space-efficient as possible, `synchronous-autocomplete` requires four indexes to be prebuilt from the list of items. They look like this for our example:
In order to be as fast and disk-space-efficient as possible, `synchronous-autocomplete` requires five indexes to be prebuilt from the list of items. For our example, they would look like this:
```js
const tokens = {
juicy: ['apple', 'banana'],
sour: ['apple', 'pomegranate'],
apple: ['apple'],
sweet: ['banana'],
banana: ['banana'],
pomegranate: ['pomegranate']
const tokens = { // internal item IDs, by token
juicy: [0, 1],
sour: [0, 3],
apple: [0],
sweet: [1],
banana: [1],
pomegranate: [3]
}
const weights = {
apple: 3,
banana: 2,
pomegranate: 5
const weights = [ // item weights, by internal item ID
3, // apple
2, // banana
5 // pome
]
const nrOfTokens = [ // nr of tokens, by internal item ID
3, // apple
3, // banana
2 // pome
]
const scores = { // "uniqueness" of each token, by token
juicy: 2 / 3, // 2 out of 3 items have the token "juicy"
sour: 2 / 3,
apple: 1 / 3,
sweet: 1 / 3,
banana: 1 / 3,
pomegranate: 1 / 3
}
const nrOfTokens = {
apple: 3,
banana: 3,
pomegranate: 2
}
const scores = {
juicy: 2 / 3, // 2 out of 3 items
sour: 2 / 3, // 2 out of 3 items
apple: 1 / 3, // 1 out of 3 items
sweet: 1 / 3, // 1 out of 3 items
banana: 1 / 3, // 1 out of 3 items
pomegranate: 1 / 3 // 1 out of 3 items
}
// In order to create smaller search indexes, we use numerical item IDs
// internally and maintain a mapping to their "real"/original IDs.
const originalIds = [
'apple',
'banana',
'pome'
]
```

@@ -116,10 +124,11 @@

```js
const autocomplete = create(tokens, scores, weights, nrOfTokens, tokenize)
const autocomplete = create(tokens, scores, weights, nrOfTokens, originalIds, tokenize)
autocomplete(query, limit = 6, fuzzy = false, completion = true)
```
`tokens` must be an object with an array of *item* IDs per *token*.
`tokens` must be an object with an array of internal *item* IDs per *token*.
`scores` must be an object with a *token* score per *token*.
`weights` must be an object with an *item* weight per *item* ID.
`nrOfTokens` must be an object with the number of *tokens* per *item* ID.
`weights` must be an array with an *item* weight per internal *item* ID.
`nrOfTokens` must be an array with the number of *tokens* per internal *item* ID.
`originalIds` must be an array with the (real) *item* ID per internal *item* ID.
`tokenize` must be a function that, given a search query, returns an array of *fragments*.

@@ -126,0 +135,0 @@

'use strict'
const roundTo = (v, p) => parseFloat(v.toFixed(p))
// todo: handle props like `toString`
const buildIndexes = (tokenize, items) => {
const originalIds = []
const tokens = Object.create(null)
const weights = Object.create(null)
const nrOfTokens = Object.create(null)
const weights = []
const nrOfTokens = []
let currentId = 0
const generateId = (oldId) => {
const newId = currentId++
originalIds[newId] = oldId
return newId
}
for (let item of items) {
const id = generateId(item.id)
const tokensOfItem = tokenize(item.name)
for (let token of tokensOfItem) {
if (!Array.isArray(tokens[token])) tokens[token] = []
if (!tokens[token].includes(item.id)) tokens[token].push(item.id)
if (!tokens[token].includes(id)) tokens[token].push(id)
}
weights[item.id] = item.weight
nrOfTokens[item.id] = tokensOfItem.length
weights[id] = item.weight
nrOfTokens[id] = tokensOfItem.length
}

@@ -22,8 +34,9 @@

const nrOfItemsForToken = tokens[token].length
scores[token] = nrOfItemsForToken / items.length
const score = nrOfItemsForToken / items.length
scores[token] = roundTo(score, 6 - Math.log10(score) | 0)
}
return {tokens, scores, weights, nrOfTokens}
return {tokens, scores, weights, nrOfTokens, originalIds}
}
module.exports = buildIndexes

@@ -6,3 +6,3 @@ 'use strict'

const createAutocomplete = (tokens, scores, weights, nrOfTokens, tokenize) => {
const createAutocomplete = (tokens, scores, weights, nrOfTokens, originalIds, tokenize) => {
const byFragment = (fragment, completion, fuzzy) => {

@@ -12,5 +12,7 @@ const results = {}

// todo: hasProp check?
if (tokens[fragment]) {
const relevance = 1 + scores[fragment] + Math.sqrt(fragment.length)
// todo: for i loop
for (let id of tokens[fragment]) {

@@ -25,2 +27,3 @@ if (!results[id] || !results[id] > relevance) {

for (let t in tokens) {
// todo: hasProp check?
if (fragment === t) continue // has been dealt with above

@@ -38,2 +41,3 @@

// todo: for i loop
for (let id of tokens[t]) {

@@ -76,3 +80,8 @@ if (!results[id] || !results[id] > relevance) {

const score = relevance * Math.pow(weights[id], 1/3)
results[id] = {id, relevance, score, weight: weights[id]}
results[id] = {
id: originalIds[id],
relevance,
score,
weight: weights[id]
}
}

@@ -79,0 +88,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc