Pixata.SimilarityChooser 2.0.0

dotnet add package Pixata.SimilarityChooser --version 2.0.0
                    
NuGet\Install-Package Pixata.SimilarityChooser -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Pixata.SimilarityChooser" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Pixata.SimilarityChooser" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Pixata.SimilarityChooser" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Pixata.SimilarityChooser --version 2.0.0
                    
#r "nuget: Pixata.SimilarityChooser, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Pixata.SimilarityChooser@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Pixata.SimilarityChooser&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Pixata.SimilarityChooser&version=2.0.0
                    
Install as a Cake Tool

Pixata.SimilarityChooser Pixata.SimilarityChooser Nuget package

Pixata

A utility that checks for similar items. Useful for when your users don't bother checking to see if the data they want already exists, and add a duplicate. The Similarity Chooser looks through your existing items and lets you know which may match.

SQL Server offers something similar with its free-text search, but this is very limited, and is restricted to known word form variants, such as colour/color and so on. It does not match typos, which makes it fairly useless when dealing with user input.

The similarity chooser uses a Metaphone search, which means it matches words phonetically. This means that if the user entered "humus" it would match "hummous," or one of various other variations. This makes it much more powerful than free-text searches.

Version 2.0.0 changes how names are matched, so that one common word (such as "Ltd") no longer makes two names match. See breaking changes in v2.0.0 below.

Breaking changes in v2.0.0

Previously, a name matched if any one of its words sounded like any word of the search text. With company names, that made almost everything match, as every "Ltd" matched every other "Ltd", and words such as "Business" and "Services" did the same. As of v2.0.0...

  • Every word has to match. A name only matches when every word of the shorter of the name and the search text is close to a word of the longer one. So "Smith Ventures" no longer matches "Advanced Ventures", but "Advance Business Ventures" still matches "Advanced Business Ventures Ltd", and searching for "maple syrup" still finds "Syrup".
  • Words have to be spelt alike as well as sound alike. The Double Metaphone key only looks at the first few sounds, so "Cavendish" and "Coventry" have the same key. Two words are now only close if, as well as sounding alike, no more than a third of the letters of the longer one need changing to get from one to the other. "Hummous" and "humus" still match.
  • Plurals match. A word of four or more letters now matches the same word with up to three letters added to the end, so "apple" matches "apples", and "advance" matches "advanced".
  • The closest matches come first. The results are ordered by how much of the name matched, so searching for "Acme" lists "Acme Ltd" before "Acme Widgets Ltd".
  • Hyphens, slashes and commas separate words. So "Smith-Jones" is now two words. Other punctuation is still removed, so "O'Brien" is one word.

You can switch the first two off if you want the old behaviour back (see options below). CreateMap takes the options as a new optional second parameter, so existing calls still compile.

Set up

Before you can look for matches, you need to set up the chooser. This is done by creating a collection of MatchingEntityOverview objects and passing it to the CreateMap method...

SimilarEntityChooser<Products> chooser = SimilarEntityChooser<Product>
  .CreateMap(appDbContext.Products
    .Select(p => new MatchingEntityOverview<Products> {
      ID = p.ProductID,
      MatchText = p.ProductName,
      Entity = p
    }));

Once you have the chooser set up, you can query it...

IEnumerable<Products> syrups = chooser
  .FindSimilar("syrup")
  .Select(meo => meo.Entity);

You can then display the choice to your users as you wish.

If you are working in a stateful environment, such as a desktop or Blazor application, where the collection can be held in memory for multiple uses, the code above is fine. However, in a stateless environment, such as a regular web application, you would need to create the map on each request. In that case, you can simplify the code by wapping it into one call...

IEnumerable<Products> syrups = SimilarEntityChooser<Products>
  .CreateMap(appDbContext.Products
    .Select(p => new MatchingEntityOverview<Products> { 
      ID = p.ProductID, 
      MatchText = p.ProductName, 
      Entity = p 
    }))
  .FindSimilar("syrup")
  .Select(meo => meo.Entity);

Options

You can pass a SimilarEntityChooserOptions to CreateMap to change how names are matched...

SimilarEntityChooser<Company> chooser = SimilarEntityChooser<Company>
  .CreateMap(appDbContext.Companies
    .Select(c => new MatchingEntityOverview<Company> {
      ID = c.Id,
      MatchText = c.Name,
      Entity = c
    }), new() {
      IgnoredWords = SimilarEntityChooserOptions.CompanyNameWords
    });
Option Default What it does
IgnoredWords None Words that are left out of both the names and the search text, as they are too common to tell two names apart. Case, accents and punctuation don't matter, so "ltd." is the same as "Ltd". A name made only of these words keeps them all, so it can still be found
MatchAllWords true Whether every word of the shorter of the name and the search text has to match. Set it to false to have one matching word be enough, as it was before v2.0.0. The results are still ordered with the closest matches first
RequireCloseSpelling true Whether words that sound alike have to be spelt alike as well. Set it to false to have sounding alike be enough, as it was before v2.0.0

SimilarEntityChooserOptions.CompanyNameWords is a ready-made list of words that are common in company names: Ltd, Limited, LLP, PLC, CIC, Inc, LLC, Co, Company, Services, Business, Management, Group, Holdings, Solutions, UK, The, And and Of. With that list, "Smith Services" matches "Smith Ltd". You can add your own...

new SimilarEntityChooserOptions {
  IgnoredWords = [.. SimilarEntityChooserOptions.CompanyNameWords, "Trading", "International"]
}

Limitations

As the matching is done phonetically, the results are not always what the user might expect. For example, on a groceries site where we use this, entering "pasta" brings up an unexpected result...

Pixata

I'm not an expert in linguists, but must of the time I can work out why a match appears, but it can confuse people. Generally, the longer the search text they enter, the better the matches, but there will always be some unexpected results. Version 2.0.0 should give fewer of these, as words now have to be spelt alike as well as sound alike, and every word has to match.

Prior to v2.0.0, the chooser didn't match plurals, so searching for "apple" would not find "apples". It does now.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 85 10/19/2026
1.0.4 351 2/23/2026
1.0.3 152 2/23/2026
1.0.1 713 5/31/2022
1.0.0 765 5/5/2021