Skip to content

Instantly share code, notes, and snippets.

@jdnichollsc
Last active July 22, 2025 01:34
Show Gist options
  • Save jdnichollsc/4929335271f71e5cd212e27f440fda26 to your computer and use it in GitHub Desktop.
Save jdnichollsc/4929335271f71e5cd212e27f440fda26 to your computer and use it in GitHub Desktop.
Customer Record Search

Customer Record Search

Ground Rules

  1. You may google references needed to complete the exercise, but you may not google the solution itself. If this happens, the result will be an automatic failure.

Criteria for Success

  1. The implementation should be full, so all methods are implemented.
  2. The implementation should be compilable, so no compilation errors in output window.
  3. The task should be completed within this hour.
  4. The implementation should be correct.
  5. No changes to the original interface are allowed.

Task

At XXX Company, we store all sorts of information about our customers' customers. Those are our C2 users. It is common that our primary customer (a plumbing company for example) needs to find one of those customer records. That's where search comes into play. This is an extremely important scenario for our customers because they may have thousands of customers in their system. It isn't always feasible to expect to find the correct records by simply sorting and manually looking for matches.

You will be provided with three sets of data. Weights will contain a field and numeric value that will be used to score search results. For example, the weight for the 'FirstName' field might be 100. Customers will contain commonly used data to identify customers: first name, last name, and address. Finally, you will be provided with a search string.

Armed with this data, your job is to write a program that will evaluate the provided search string and return the top 5 customer search results. The rules for ranking results are as follows:

  1. All searches are case-insensitive.
  2. The score of each search result is the sum of scores of its individual fields.
  3. The search string can be used in full or in part (split into multiple tokens), which is used in the field scoring function below. Note: The tokenization method has been provided for simplicity.
  4. The score of a field is calculated as follows:
    1. If a field contains the full search string, it will evaluate as 400% of its weight.
    2. If a field exactly matches the text of any search token, it will evaluate as 200% of its weight.
    3. If a field contains the text of any search token, it will evaluate as 100% of its weight.
  5. A token may match multiple fields and the weight for each is evaluated in isolation.```
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Solution;
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
}
public class SearchResult
{
public Customer Customer { get; set; }
public int Score { get; set; }
}
class Solution {
static void Main(string[] args) {
using var stdinStream = Console.OpenStandardInput();
using var stdin = new StreamReader(stdinStream);
var lines = stdin.ReadToEnd().Split(Environment.NewLine)
.Select(l => {
return l.Split(',');
})
.ToList();
var weights = lines.Where(l => l[0] == "weight")
.ToDictionary(x => x[1], x => int.Parse(x[2]));
var customers = lines.Where(l => l[0] == "customer")
.Select(l => new Customer {
FirstName = l[1],
LastName = l[2],
Address = l[3]
})
.ToList();
var searchString = lines.Where(l => l[0] == "search")
.Select(l => l[1])
.First();
// perform the search
var searchResults = Search(weights, customers, searchString);
// display the results
foreach (var result in searchResults)
{
Console.WriteLine($"{result.Customer.FirstName} {result.Customer.LastName} {result.Customer.Address} - Score: {result.Score}");
}
}
static List<string> TokenizeSearchString(string searchString)
{
// tokenize the search string
return searchString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Where(token => token.Length >= 3)
.ToList();
}
static List<SearchResult> Search(Dictionary<string, int> weights, List<Customer> customers, string searchString)
{
var results = new List<SearchResult>();
// TODO: Implement here...
var searchValue = searchString.ToLowerInvariant();
var tokens = TokenizeSearchString(searchValue);
foreach (var customer in customers)
{
var totalScore = 0;
totalScore += CalculateFieldScore(weights, searchValue, tokens, (nameof(Customer.FirstName), customer.FirstName));
totalScore += CalculateFieldScore(weights, searchValue, tokens, (nameof(Customer.LastName), customer.LastName));
totalScore += CalculateFieldScore(weights, searchValue, tokens, (nameof(Customer.Address), customer.Address));
if (totalScore > 0)
{
results.Add(new SearchResult { Customer = customer, Score = totalScore });
}
}
return results
.OrderByDescending(r => r.Score)
// .ThenBy(r => r.Customer.FirstName) -> deterministic results in real-world scenarios
.Take(5)
.ToList();
}
static int CalculateFieldScore(
Dictionary<string, int> weights,
string search,
List<string> tokens,
(string Name, string Value) field)
{
if (string.IsNullOrEmpty(field.Value))
return 0;
var weight = weights[field.Name];
var value = field.Value.ToLowerInvariant();
// 400% if full search string is found (Cedar St)
if (value.Contains(search))
return weight * 4;
// 200% if equals any token exactly
foreach (var token in tokens)
{
if (value == token)
return weight * 2;
}
// 100% if contains any token
foreach (var token in tokens)
{
if (value.Contains(token))
return weight;
}
return 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment