An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times.
The IsIsogram() method takes a string and returns boolean value.
- INPUT: anik OUTPUT: true
- INPUT: A nika OUTPUT: false
Approach 01: Using Dictionary<TKey, TValue>
static bool IsIsogram(string str)
{
//create a dictionary with 26 keys and assign false as default value
var storeOccurance = new Dictionary<int, bool>();
for (int i = 0; i <= 25; i++)
{
storeOccurance[i] = false;
}
str = Regex.Replace(str, "[ -]", ""); //ignore whitespace and dash
//iterate over each character of the string
foreach (var ch in str.ToLower())
{
//(ch - 'a') is used to calculate the key. Check if the character key has false as it's value,
//meaning they were never stored
if (!storeOccurance[ch - 'a'])
{
storeOccurance[ch - 'a'] = true; //assign true when they are stored in the dictionary
}
else //otherwise, if a character key has true as it's value then it means that it exists in the dictionary
{
return false; //return false and exits the iteration..Multiple occurance in dictionary
//means the string has repeating character so it's not Isogram
}
}
return true;
}
Approach 02: Using HashSet<T>
static bool IsIsogram(string str)
{
//TRICK: HashSet<T>.Add(T) returns false if an item already exists in HashSet<T>,
//otherwise returns true and add the character to the datastructure
var charHash = new HashSet<char>();
foreach (var ch in str.ToLower())
{
//check if the charHash.Add() returns false, if it does then terminate
//as it means the character already exists
if (!charHash.Add(ch) && char.IsLetter(ch))
return false;
}
return true; //otherwise return true
}
Approach 03: Using LINQ
static bool IsIsogram(string str)
{ //Suppose "anik a" is a given string
return str.Where(char.IsLetter) //filter only the letters ||here- only a, n, i, k, a will be taken
.GroupBy(char.ToLower) //create group by characters(and transform them to lowercase) || here, there will be a group for each character a, n, i, k
.All(g => g.Count() == 1); //for every group, count the number of it's element and check if it's 1
//|| here, 'a' group has 2 elements so it return false though other groups has only one element in their group
}
Given those above approaches:
- Which approach would you recommend regarding readability, clean code and performance?
- Is there any scope for improvement in the existing approaches?
return str.Count() == str.distinct().count();\$\endgroup\$var uniques = new HashSet<char>(); return str.Where(char.IsLetter).Select(char.ToLower).All(uniques.Add);\$\endgroup\$