パッケージの詳細

quick-score

fwextensions11kMIT0.2.0

A JavaScript string-scoring and fuzzy-matching library based on the Quicksilver algorithm, designed for smart auto-complete.

string, score, sort, search

readme

QuickScore

quick-score is a JavaScript string-scoring and fuzzy-matching library based on the Quicksilver algorithm, designed for smart auto-complete.

Code Coverage Build Status Minzip Size MIT License

QuickScore improves on the original Quicksilver algorithm by tuning the scoring for long strings, such as webpage titles or URLs, so that the order of the search results makes more sense. It's used by the QuicKey extension for Chrome to enable users to easily find an open tab via search.

QuickScore is fast, dependency-free, and is just 2KB when minified and gzipped.

Demo

See QuickScore in action, and compare its results to other scoring and matching libraries.

Install

npm install --save quick-score

If you prefer to use the built library files directly instead of using npm, you can download them from https://unpkg.com/browse/quick-score/dist/.

Or you can load a particular release of the minified script directly from unpkg.com, and then access the library via the quickScore global:

<script src="https://unpkg.com/quick-score@0.0.14/dist/quick-score.min.js"></script>
<script type="text/javascript">
    console.log(quickScore.quickScore("thought", "gh"));
</script>

Usage

Calling quickScore() directly

You can import the quickScore() function from the ES6 module:

import {quickScore} from "quick-score";

Or from a property of the CommonJS module:

const quickScore = require("quick-score").quickScore;

Then call quickScore() with a string and a query to score against that string. It will return a floating point score between 0 and 1. A higher score means that string is a better match for the query. A 1 means the query is the highest match for the string, though the two strings may still differ in case and whitespace characters.

quickScore("thought", "gh");   // 0.4142857142857143
quickScore("GitHub", "gh");    // 0.9166666666666666

Matching gh against GitHub returns a higher score than thought, because it matches the capital letters in GitHub, which are weighted more highly.

Sorting lists of strings with a QuickScore instance

A typical use-case for string scoring is auto-completion, where you want the user to get to the desired result by typing as few characters as possible. Instead of calling quickScore() directly for every item in a list and then sorting it based on the score, it's simpler to use an instance of the QuickScore class:

import {QuickScore} from "quick-score";

const qs = new QuickScore(["thought", "giraffe", "GitHub", "hello, Garth"]);
const results = qs.search("gh");

// results =>
[
    {
        "item": "GitHub",
        "score": 0.9166666666666666,
        "matches": [[0, 1], [3, 4]]
    },
    {
        "item": "hello, Garth",
        "score": 0.6263888888888888,
        "matches": [[7, 8], [11, 12]]
    },
    // ...
]

The results array in this example is a list of ScoredString objects that represent the results of matching the query against each string that was passed to the constructor. It's sorted high to low on each item's score. Strings with identical scores are sorted alphabetically and case-insensitively. In the simple case of scoring bare strings, each ScoredString item has three properties:

  • item: the string that was scored
  • score: the floating point score of the string for the current query
  • matches: an array of arrays that specify the character ranges where the query matched the string

This array could then be used to render a list of matching results as the user types a query.

Sorting lists of objects

Typically, you'll be sorting items more complex than a bare string. To tell QuickScore which of an object's keys to score a query against, pass an array of key names or dot-delimited paths as the second parameter to the QuickScore() constructor:

const bookmarks = [
    {
        "title": "lodash documentation",
        "url": "https://lodash.com/docs"
    },
    {
        "title": "Supplying Images - Google Chrome",
        "url": "developer.chrome.com/webstore/images"
    },
    // ...
];
const qs = new QuickScore(bookmarks, ["title", "url"]);
const results = qs.search("devel");

// results =>
[
    {
        "item": {
            "title": "Supplying Images - Google Chrome",
            "url": "developer.chrome.com/webstore/images"
        },
        "score": 0.9138888888888891,
        "scoreKey": "url",
        "scores": {
            "title": 0,
            "url": 0.9138888888888891
        },
        "matches": {
            "title": [],
            "url": [[0, 5]]
        }
    },
    // ...
]

When matching against objects, each item in the results array is a ScoredObject, with a few additional properties :

  • item: the object that was scored
  • score: the highest score from among the individual key scores
  • scoreKey: the name of the key with the highest score, which will be an empty string if they're all zero
  • scoreValue: the value of the key with the highest score, which makes it easier to access if it's a nested string
  • scores: a hash of the individual scores for each key
  • matches: a hash of arrays that specify the character ranges of the query match for each key

When two items have the same score, they're sorted alphabetically and case-insensitively on the key specified by the sortKey option, which defaults to the first item in the keys array. In the example above, that would be title.

Each ScoredObject item also has a _ property, which caches transformed versions of the item's strings, and might contain additional internal metadata in the future. It can be ignored.

TypeScript support

Although the QuickScore codebase is currently written in JavaScript, the package comes with full TypeScript typings. The QuickScore class takes a generic type parameter based on the type of objects in the items array passed to the constructor. That way, you can access .item on the ScoredObject result and get back an object of the same type that you passed in.

Ignoring diacritics and accents when scoring

If the strings you're matching against contain diacritics on some of the letters, like à or ç, you may want to count a match even when the query string contains the unaccented forms of those letters. The QuickScore library doesn't contain support for this by default, since it's only needed with certain strings and the code to remove accents would triple its size. But it's easy to combine QuickScore with other libraries to ignore diacritics.

One example is the latinize npm package, which will strip accents from a string and can be used in a transformString() function that's passed as an option to the QuickScore constructor. This function takes a string parameter and returns a transformed version of that string:

// including latinize.js on the page creates a global latinize() function
import {QuickScore} from "quick-score";

const items = ["Café", "Cafeteria"];
const qs = new QuickScore(items, { transformString: s => latinize(s).toLowerCase() });
const results = qs.search("cafe");

// results =>
[
    {
        "item": "Café",
        "score": 1,
        "matches": [[0, 4]],
        "_": "cafe"
    },
    // ...
]

transformString() will be called on each of the searchable keys in the items array as well as on the query parameter to the search() method. The default function calls toLocaleLowerCase() on each string, for a case-insensitive search. In the example above, the basic toLowerCase() call is sufficient, since latinize() will have already stripped any accents.

Highlighting matched letters

Many search interfaces highlight the letters in each item that match what the user has typed. The matches property of each item in the results array contains information that can be used to highlight those matching letters.

The functional component below is an example of how an item could be highlighted using React. It surrounds each sequence of matching letters in a <mark> tag and then returns the full string in a <span>. You could then style the <mark> tag to be bold or a different color to highlight the matches. (Something similar could be done by concatenating plain strings of HTML tags, though you'll need to be careful to escape the substrings.)

function MatchedString({ string, matches }) {
    const substrings = [];
    let previousEnd = 0;

    for (let [start, end] of matches) {
        const prefix = string.substring(previousEnd, start);
        const match = <mark>{string.substring(start, end)}</mark>;

        substrings.push(prefix, match);
        previousEnd = end;
    }

    substrings.push(string.substring(previousEnd));

    return <span>{React.Children.toArray(substrings)}</span>;
}

The QuickScore demo uses this approach to highlight the query matches, via the MatchedString component.

API

See the API docs for a full description of the QuickScore class and the quickScore function.

License

MIT © John Dunning

更新履歴

Changelog

0.2.0 - 2022-6-03

Fixed

  • Improve TypeScript compatibility by typing the QuickScore array params as readonly.
  • Type the item key in both ScoredString and ScoredObject as a generic so ScoredResult is more consistent.
  • Update 'devDependencies' to the latest major/minor versions, including Jest 28.

0.1.0 - 2022-04-19

Added

  • Add TypeScript support via index.d.ts declaration file, and add types field to package.json.
  • Update JSDocs to include type information.

Changed

  • Don't concat the parameters to setItems() and setKeys() with an empty array. Call .slice() instead to create a copy. This means that passing a single bare string instead of an array to the QuickScore constructor will no longer work.
  • Don't supply a default value for the sortKey parameter in setKeys().
  • Don't supply default values for the string and query parameters in quickScore().

Fixed

  • Resolve #19 and #20.
  • Remove empty PR trigger in gh-pages.yml.
  • Improve API docs styling.
  • Update devDependencies to the latest minor versions.

0.0.14 - 2022-02-24

Fixed

  • Update devDependencies to the latest versions.
  • Add GitHub action to push docs to GitHub Pages and code coverage to Codecov.

0.0.13 - 2021-10-05

Fixed

  • Update devDependencies to the latest minor versions.
  • Run npm audit fix to remove vulnerabilities.
  • Update .travis.yml to enable partner queue builds.
  • Add GitHub code analysis workflow.

0.0.12 - 2021-04-24

Fixed

  • Limit the number of loops inside the quickScore() function so that long, nearly-matching queries don't take too long before returning a 0 score. Added config.maxIterations to control the number of loops.
  • Update devDependencies to latest packages.

0.0.11 - 2021-03-26

Added

  • Passing an empty array in the keys parameter will cause all of the keys on an item to be cached and searched, without having to specify each one.
  • Paths to nested keys in the keys array can be specified as arrays of strings, instead of a dot-delimited path in a single string. Wrapping a single string in an array will cause any dots it contains to not be treated as a path.
  • A new sortKey option can be used to specify on which key to sort identically-scored items, if a key other than the first one in keys is desired.
  • A new scoreValue field is returned in the results from search(), which provides the string pointed to be scoreKey. This makes it easier to access the string when it's nested.

0.0.10 - 2021-01-02

Added

  • A new transformString option to the QuickScore constructor can be used to ignore diacritics and accents when searching.

Fixed

  • Update devDependencies to latest packages, fixing a vulnerability in jest.

0.0.9 - 2020-07-25

Fixed

  • Update devDependencies to latest packages.

0.0.8 - 2020-05-07

Fixed

  • Use the correct unpkg.com CDN URL in the readme.
  • Highlight needing to access the methods through a global when loading the library via a <script> tag.

0.0.7 - 2020-05-03

Added

  • Info about loading built files from unpkg.com to the readme.
  • Add a test that doesn't pass a matches array to get the branch coverage to 100%.
  • Added CHANGELOG.md.

Fixed

  • Update devDependencies to latest packages.
  • Update node version in .travis.yml to latest.
  • Update to rollup 2.0.

0.0.6 - 2020-01-19

Fixed

  • The correct match indices were not returned in some cases, since the whole matches array was getting cleared when remainingScore was 0 during a search.
  • Update devDependencies to latest packages.

0.0.5 - 2019-10-19

Fixed

  • Update devDependencies to latest packages.

0.0.4 - 2019-05-27

Fixed

  • Update to babel 7, jest 24, and rollup 1.0.
  • Update devDependencies to latest packages.

0.0.3 - 2019-05-27

Added

  • Support searching nested keys on objects use dot-delimited key names, like "foo.bar".

0.0.2 - 2018-11-05

Fixed

  • Changed prepare script to prepublishOnly.

0.0.1 - 2018-11-05

Added

  • Initial release.