Is there an algorith or C# library to determine if a human name is correct or not and, if not, to find its nearest matching?
I found algorithms for string matching like the Levenshtein's distance algorithm, but all of them check the matching between one string and another, and i want to check the matching between one name and all the possible names in English (for example), to check if the name was wrongly written.
For example:
Someone inserts the name "Giliam" while it should be "william". I want to know if there are any algorithm (or group of them) to detect the error and propose a correction.
All solutions that come to my mind involves the implementation of a huge human name's dictionary and use it to check for the correctness of each input name matching one by one... And it sounds terrorific performace to me, so i want to ask for a better approach.
Thanks.
What you are in effect asking is how to create a spell checker with a given dictionary. One way to do this that doesn't involve looking up and testing every possible entry in a list is to do the inverse of the problem: Generate a list of possible permutations from the user input, and test each one of those to see if they're in a list. This is a much more manageable problem.
For instance, you could use a function like this to generate each possible permutation that one "edit" could get from a given word:
static HashSet<string> GenerateEdits(string word)
{
// Normalize the case
word = word.ToLower();
var splits = new List<Tuple<string, string>>();
for (int i = 0; i < word.Length; i++)
{
splits.Add(new Tuple<string, string>(word.Substring(0, i), word.Substring(i)));
}
var ret = new HashSet<string>();
// All cases of one character removed
foreach (var cur in splits)
{
if (cur.Item2.Length > 0)
{
ret.Add(cur.Item1 + cur.Item2.Substring(1));
}
}
// All transposed possibilities
foreach (var cur in splits)
{
if (cur.Item2.Length > 1)
{
ret.Add(cur.Item1 + cur.Item2[1] + cur.Item2[0] + cur.Item2.Substring(2));
}
}
var letters = "abcdefghijklmnopqrstuvwxyz";
// All replaced characters
foreach (var cur in splits)
{
if (cur.Item2.Length > 0)
{
foreach (var letter in letters)
{
ret.Add(cur.Item1 + letter + cur.Item2.Substring(1));
}
}
}
// All inserted characters
foreach (var cur in splits)
{
foreach (var letter in letters)
{
ret.Add(cur.Item1 + letter + cur.Item2);
}
}
return ret;
}
And then exercise the code to see if a given user input can be easily convoluted to one of these entries. Finding the best match can be done by weighted averages, or simply by presenting the list of possibilities to the user:
// Example file from:
// https://raw.githubusercontent.com/smashew/NameDatabases/master/NamesDatabases/first%20names/all.txt
string source = #"all.txt";
var names = new HashSet<string>();
using (var sr = new StreamReader(source))
{
string line;
while ((line = sr.ReadLine()) != null)
{
names.Add(line.ToLower());
}
}
var userEntry = "Giliam";
var found = false;
if (names.Contains(userEntry.ToLower()))
{
Console.WriteLine("The entered value of " + userEntry + " looks good");
found = true;
}
if (!found)
{
// Try edits one edit away from the user entry
foreach (var test in GenerateEdits(userEntry))
{
if (names.Contains(test))
{
Console.WriteLine(test + " is a possibility for " + userEntry);
found = true;
}
}
}
if (!found)
{
// Try edits two edits away from the user entry
foreach (var test in GenerateEdits(userEntry))
{
foreach (var test2 in GenerateEdits(test))
{
if (names.Contains(test))
{
Console.WriteLine(test + " is a possibility for " + userEntry);
found = true;
}
}
}
}
kiliam is a possibility for Giliam
liliam is a possibility for Giliam
viliam is a possibility for Giliam
wiliam is a possibility for Giliam
Of course, since you're talking about human names, you had, at best, make this a suggestion, and be very prepared for odd spellings, and spellings of things you've never seen. And if you want to support other languages, the implementation of GenerateEdits gets more complex as you consider what counts for a 'typo'
Related
I have a text file full of strings, one on each line. Some of these strings will contain an unknown number of "#" characters. Each "#" can represent the numbers 1, 2, 3, or 4. I want to generate all possible combinations (permutations?) of strings for each of those "#"s. If there were a set number of "#"s per string, I'd just use nested for loops (quick and dirty). I need help finding a more elegant way to do it with an unknown number of "#"s.
Example 1: Input string is a#bc
Output strings would be:
a1bc
a2bc
a3bc
a4bc
Example 2: Input string is a#bc#d
Output strings would be:
a1bc1d
a1bc2d
a1bc3d
a1bc4d
a2bc1d
a2bc2d
a2bc3d
...
a4bc3d
a4bc4d
Can anyone help with this one? I'm using C#.
This is actually a fairly good place for a recursive function. I don't write C#, but I would create a function List<String> expand(String str) which accepts a string and returns an array containing the expanded strings.
expand can then search the string to find the first # and create a list containing the first part of the string + expansion. Then, it can call expand on the last part of the string and add each element in it's expansion to each element in the last part's expansion.
Example implementation using Java ArrayLists:
ArrayList<String> expand(String str) {
/* Find the first "#" */
int i = str.indexOf("#");
ArrayList<String> expansion = new ArrayList<String>(4);
/* If the string doesn't have any "#" */
if(i < 0) {
expansion.add(str);
return expansion;
}
/* New list to hold the result */
ArrayList<String> result = new ArrayList<String>();
/* Expand the "#" */
for(int j = 1; j <= 4; j++)
expansion.add(str.substring(0,i-1) + j);
/* Combine every expansion with every suffix expansion */
for(String a : expand(str.substring(i+1)))
for(String b : expansion)
result.add(b + a);
return result;
}
I offer you here a minimalist approach for the problem at hand.
Yes, like other have said recursion is the way to go here.
Recursion is a perfect fit here, since we can solve this problem by providing the solution for a short part of the input and start over again with the other part until we are done and merge the results.
Every recursion must have a stop condition - meaning no more recursion needed.
Here my stop condition is that there are no more "#" in the string.
I'm using string as my set of values (1234) since it is an IEnumerable<char>.
All other solutions here are great, Just wanted to show you a short approach.
internal static IEnumerable<string> GetStrings(string input)
{
var values = "1234";
var permutations = new List<string>();
var index = input.IndexOf('#');
if (index == -1) return new []{ input };
for (int i = 0; i < values.Length; i++)
{
var newInput = input.Substring(0, index) + values[i] + input.Substring(index + 1);
permutations.AddRange(GetStrings(newInput));
}
return permutations;
}
An even shorter and cleaner approach with LINQ:
internal static IEnumerable<string> GetStrings(string input)
{
var values = "1234";
var index = input.IndexOf('#');
if (index == -1) return new []{ input };
return
values
.Select(ReplaceFirstWildCardWithValue)
.SelectMany(GetStrings);
string ReplaceFirstWildCardWithValue(char value) => input.Substring(0, index) + value + input.Substring(index + 1);
}
This is shouting out loud for a recursive solution.
First, lets make a method that generates all combinations of a certain length from a given set of values. Because we are only interested in generating strings, lets take advantage of the fact that string is immutable (see P.D.2); this makes recursive functions so much easier to implement and reason about:
static IEnumerable<string> GetAllCombinations<T>(
ISet<T> set, int length)
{
IEnumerable<string> getCombinations(string current)
{
if (current.Length == length)
{
yield return current;
}
else
{
foreach (var s in set)
{
foreach (var c in getCombinations(current + s))
{
yield return c;
}
}
}
}
return getCombinations(string.Empty);
}
Study carefully how this methods works. Work it out by hand for small examples to understand it.
Now, once we know how to generate all possible combinations, building the strings is easy:
Figure out the number of wildcards in the specified string: this will be our combination length.
For every combination, insert in order each character into the string where we encounter a wildcard.
Ok, lets do just that:
public static IEnumerable<string> GenerateCombinations<T>(
this string s,
IEnumerable<T> set,
char wildcard)
{
var length = s.Count(c => c == wildcard);
var combinations = GetAllCombinations(set, length);
var builder = new StringBuilder();
foreach (var combination in combinations)
{
var index = 0;
foreach (var c in s)
{
if (c == wildcard)
{
builder.Append(combination[index]);
index += 1;
}
else
{
builder.Append(c);
}
}
yield return builder.ToString();
builder.Clear();
}
}
And we're done. Usage would be:
var set = new HashSet<int>(new[] { 1, 2, 3, 4 });
Console.WriteLine(
string.Join("; ", "a#bc#d".GenerateCombinations(set, '#')));
And sure enough, the output is:
a1bc1d; a1bc2d; a1bc3d; a1bc4d; a2bc1d; a2bc2d; a2bc3d;
a2bc4d; a3bc1d; a3bc2d; a3bc3d; a3bc4d; a4bc1d; a4bc2d;
a4bc3d; a4bc4d
Is this the most performant or efficient implementation? Probably not but its readable and maintainable. Unless you have a specific performance goal you are not meeting, write code that works and is easy to understand.
P.D. I’ve omitted all error handling and argument validation.
P.D.2: if the length of the combinations is big, concatenting strings inside GetAllCombinations might not be a good idea. In that case I’d have GetAllCombinations return an IEnumerable<IEnumerable<T>>, implement a trivial ImmutableStack<T>, and use that as the combination buffer instead of string.
I have an object class "Character" with a hashtable inside of it for weapons. I'm just trying to print the weapon to the console when I print the character stats for approval, but having a hard time figuring out a simple way to do this.
Advs is the Character object array, if there's any information I forgot to include please let me know, first time posting. The i.weapons.value is what I'm trying to fix, everything else prints to console correctly, that gives me strange values that look like pointers to the hashtable on print.
private static void ChStats(Character[] Advs) {
foreach (Character i in Advs) {
Console.WriteLine("Name: {0}\nClass: {1}\nLevel: {2}\nHealth: {3}\nStrength: {4}\nIntellect: {5}\nAgility: {6}\nSanity: {7}\nWeapon: {8}\n",
i.name, i.characterClass, i.lvl, i.hlth, i.str, i.itl, i.agi, i.san, i.weapons.value);
}
as an addition, the hash table contains two values for each weapon wNme and dmg. The code below works, but seem so sloppy, hopefully there's a better way to accomplish this.
foreach (Character i in Advs) {
string[] weaponDesc = new string[10];
int n = 0;
foreach (Weapon w in i.weapons.Values)
{
weaponDesc[n++] = w.wNme;
}
// this version works but with extra commas
//Console.WriteLine("Name: {0}\nClass: {1}\nLevel: {2}\nHealth: {3}\nStrength: {4}\nIntellect: {5}\nAgility: {6}\nSanity: {7}\nWeapon: {8}\n",
// i.name, i.characterClass, i.lvl, i.hlth, i.str, i.itl, i.agi, i.san, string.Join(", ", weaponDesc));
// better but more complex
Console.WriteLine("Name: {0}\nClass: {1}\nLevel: {2}\nHealth: {3}\nStrength: {4}\nIntellect: {5}\nAgility: {6}\nSanity: {7}\nWeapon: {8}\n",
i.name, i.characterClass, i.lvl, i.hlth, i.str, i.itl, i.agi, i.san, string.Join(", ", weaponDesc.Where(s => !string.IsNullOrEmpty(s))));
Generics typed HashSet class is a better choice.
HashSet<Weapon> Weapons;
Weapons = new HashSet<Weapon>();
Weapons.Add(new Weapon()); // Assume this is created and populated elsewhere
StringBuilder SBText = new StringBuilder(64 + (Weapons.Count * 8)); // An approximation of the eventual string length
SBText.Append(#"Class, Level, etc...");
// Cryptic but compact Lync method (Not my favourite)
Weapons.Where(Wpn => !string.IsNullOrEmpty(Wpn.name)).ToList().ForEach(delegate (Weapon Wpn) { if (SBText.Length > 0) { SBText.Append(#", "); } SBText.Append(Wpn.name); });
// Explicit code method
foreach (Weapon Wpn in Weapons)
{
if (!string.IsNullOrEmpty(Wpn.name))
{
if (SBText.Length > 0) { SBText.Append(#", "); }
SBText.Append(Wpn.name);
}
}
Console.WriteLine(SBText.ToString());
I'm sorry in advance if it's bad to ask for this sort of help... but I don't know who else to ask.
I have an assignment to read two text files, and find the 10 longest words in the first file (and the amount of times they're repeated) which dont exist in the second file.
I currently read both of the files with File.ReadAllLines then split them into arrays, where every element is a single word (punctuation marks removed as well) and removed empty entries.
The idea I had to pick out the words fitting the requirements was: to make a dictionary containing a string Word and an int Count. Then make a loop repeating for the first file's length.... firstly comparing the element with the entire dictionary - if it finds a match, increase the Count by 1. Then if it doesn't match with any of the dictionary elements - compare the given element with every element in the 2nd file through another loop, if it finds a match - just go on to the next element of the first file, if it doesn't find any matches - add the word to the dictionary, and set Count to 1.
So my first question is: Is this actually the most efficient way to do this? (Don't forget I've only recently started studying c# and am not allowed to use linq)
Second question: How do I work with the dictionary, because most of the results I could find were very confusing, and we have not yet met them at university.
My code so far:
// Reading and making all the words lowercase for comparisons
string punctuation = " ,.?!;:\"\r\n";
string Read1 = File.ReadAllText("#\\..\\Book1.txt");
Read1 = Read1.ToLower();
string Read2 = File.ReadAllText("#\\..\\Book2.txt");
Read2 = Read2.ToLower();
//Working with the 1st file
string[] FirstFileWords = Read1.Split(punctuation.ToCharArray());
var temp1 = new List<string>();
foreach (var word in FirstFileWords)
{
if (!string.IsNullOrEmpty(word))
temp1.Add(word);
}
FirstFileWords = temp1.ToArray();
Array.Sort(FirstFileWords, (x, y) => y.Length.CompareTo(x.Length));
//Working with the 2nd file
string[] SecondFileWords = Read2.Split(punctuation.ToCharArray());
var temp2 = new List<string>();
foreach (var word in SecondFileWords)
{
if (!string.IsNullOrEmpty(word))
temp2.Add(word);
}
SecondFileWords = temp2.ToArray();
Well I think you've done very well so far. Not being able to use Linq here is torture ;)
As for performance, you should consider making your SecondFileWords a HashSet<string> as this would increase lookup times if any word exists in the 2nd file tremendously without much effort. I wouldn't go much further in terms of performance optimization for an exercise like that if performance is not a key requirement.
Of course, you would have to check that you don't add duplicates to your 2nd list, so change your current implementation to something like:
HashSet<string> temp2 = new HashSet<string>();
foreach (var word in SecondFileWords)
{
if (!string.IsNullOrEmpty(word) && !temp2.Contains(word))
{
temp2.Add(word);
}
}
Don't convert this back to an Array again, this is not necessary.
This brings me back to your FirstFileWords which would contain duplicates too. This will cause issues later on when the top words might contain the same word multiple times. So let's get rid of them. Here it's more complicated as you need to retain the information how often a word appeared in your first list.
So let's bring a Dictionary<string, int> into play here now. A Dictionary stores a lookup key, as the HashSet, but in addition, also a value. We will use the key for the word, and the value for a number that contains the amount of how often the word appeared in the first list.
Dictionary<string, int> temp1 = new Dictionary<string, int>();
foreach (var word in FirstFileWords)
{
if (string.IsNullOrEmpty(word))
{
continue;
}
if (temp1.ContainsKey(word))
{
temp1[word]++;
}
else
{
temp1.Add(word, 1);
}
}
Now a dictionary cannot be sorted, which complicates things at this point as you still need to get your sorting by word length done. So let's get back to your Array.Sort method which I think is a good choice when you are not allowed to use Linq:
KeyValuePair<string, int>[] firstFileWordsWithCount = temp1.ToArray();
Array.Sort(firstFileWordsWithCount, (x, y) => y.Key.Length.CompareTo(x.Key.Length));
Note: You are using .ToArray() in your example, so I think it's OK to use it. But strictly speaking, this would also fall unter using Linq IMHO.
Now all that's left is working through your firstFileWordsWithCount array until you got 10 words that do not exist in the HashSet temp2. Something like:
int foundWords = 0;
foreach(KeyValuePair<string, int> candidate in firstFileWordsWithCount)
{
if (!temp2.Contains(candidate.Key))
{
Console.WriteLine($"{candidate.Key}: {candidate.Value}");
foundWords++;
}
if (foundWords >= 10)
{
break;
}
}
If anything is unclear, just ask.
This is what you'll get when using dictionaries:
string File1 = "AMD Intel Skylake Processors Graphics Cards Nvidia Architecture Microprocessor Skylake SandyBridge KabyLake";
string File2 = "Graphics Nvidia";
Dictionary<string, int> Dic = new Dictionary<string, int>();
string[] File1Array = File1.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
Array.Sort(File1Array, (s1, s2) => s2.Length.CompareTo(s1.Length));
foreach (string s in File1Array)
{
if (Dic.ContainsKey(s))
{
Dic[s]++;
}
else
{
Dic.Add(s, 1);
}
}
string[] File2Array = File2.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (string s in File2Array)
{
if (Dic.ContainsKey(s))
{
Dic.Remove(s);
}
}
int i = 0;
foreach (KeyValuePair<string, int> kvp in Dic)
{
i++;
Console.WriteLine(kvp.Key + " " + kvp.Value);
if (i == 9)
{
break;
}
}
My earlier attempt was using LINQ, which is apparently not allowed but missed it.
string[] Results = File1.Split(" ".ToCharArray()).Except(File2.Split(" ".ToCharArray())).OrderByDescending(s => s.Length).Take(10).ToArray();
for (int i = 0; i < Results.Length; i++)
{
Console.WriteLine(Results[i] + " " + Regex.Matches(File1, Results[i]).Count);
}
I am fairly new to coding so I guess I´m not seeing the obvious answer by myself so I am sorry if this is a silly question but I´m really stucked here. I am trying to compare two sets of trigrams from two different texts (A and B). If there are no trigrams in B present on A, then I would say those two texts are different, at least for my present purpose. I am using Nuve for trigram extraction.
So far I have this:
var paragraph = "This is not a phrase. This is not a sentence.";
var paragraph2 = "This is a phrase. This is a sentence. This have nothing to do with sentences.";
ITokenizer tokenizer = new ClassicTokenizer(true);
SentenceSegmenter segmenter = new TokenBasedSentenceSegmenter(tokenizer);
var sentences = segmenter.GetSentences(paragraph);
ITokenizer tokenizer2 = new ClassicTokenizer(true);
SentenceSegmenter segmenter2 = new TokenBasedSentenceSegmenter(tokenizer2);
var sentences2 = segmenter2.GetSentences(paragraph2);
var extractor = new NGramExtractor(3);
var grams1 = extractor.ExtractAsList(sentences);
var grams2 = extractor.ExtractAsList(sentences2);
var nonintersect = grams2.Except(grams1);
foreach (var nGram in nonintersect)
{
var current = nGram;
bool found = false;
foreach (var n in grams2)
{
if (!found)
{
if (n == current)
{
found = true;
}
}
}
if (!found)
{
var result = current;
string finalresult = Convert.ToString(result);
textBox3.AppendText(finalresult+ "\n");
}
This way I hope to get the sentences that, being in B, are not present in A (i.e. all the sentences from B in the example), but now I would have to compare each trigram from B to each trigram from A to see if sentences are really different from each other. I have tried to do so with another nested foreach but I get just nonsense data, as follows:
foreach (var sentence2 in sentences2)
{
var actual = sentence2;
bool found1 = false;
foreach (var sentence in sentences)
{
if (!found1)
{
if (actual == sentence)
{
found1 = true;
}
}
}
if (!found1)
{
string finalresult= Convert.ToString(actual);
textBox3.AppendText(finalresult+ "\n");
}
}
Doing this I try to verify if the trigrams from each sentence in B are equal to the trigrams from each sentence in A and, if they are, then textBox3 would be empty.
Briefly, I am trying to code something similar to Ferret but for C# and only for comparing two given plain texts. As far as I know, there is nothing similar already done for C#.
Any help or tip would be really appreciated. Thanks!
Comparing bodies of text
Comparing two bodies of text and marking them as similar if they have at least one sentence-level trigram in common is fairly straight-forward:
public bool AreTextsSimilar(string a, string b)
{
// We can reuse these objects - they could be stored in member fields:
ITokenizer tokenizer = new ClassicTokenizer(true);
SentenceSegmenter segmenter = new TokenBasedSentenceSegmenter(tokenizer);
NGramExtractor trigramExtractor = new NGramExtractor(3);
IEnumerable<string> sentencesA = segmenter.GetSentences(a);
IEnumerable<string> sentencesB = segmenter.GetSentences(b);
// The order of trigrams doesn't matter, so we'll fetch them as sets instead,
// to make comparisons between their elements more efficient:
ISet<NGram> trigramsA = trigramExtractor.ExtractAsSet(sentencesA);
ISet<NGram> trigramsB = trigramExtractor.ExtractAsSet(sentencesB);
// 'Intersect' returns all elements that are found in both collections:
IEnumerable<NGram> sharedTrigrams = trigramsA.Intersect(trigramsB);
// 'Any' only returns true if the collection isn't empty:
return sharedTrigrams.Any();
}
Without Linq methods (Intersect, Any), those last two lines could be implemented as a loop:
foreach (NGram trigramA in trigramsA)
{
// As soon as we find a shared sentence trigram we can conclude that
// the two bodies of text are indeed similar:
if (trigramsB.Contains(trigramA))
return true;
}
return false;
}
Sentences without shared word trigrams
Retrieving all sentences that do not share word-level trigrams takes some more work:
public IEnumerable<string> GetUniqueBSentences(string a, string b)
{
// We can reuse these objects - they could be stored in member fields:
ITokenizer tokenizer = new ClassicTokenizer(true);
SentenceSegmenter segmenter = new TokenBasedSentenceSegmenter(tokenizer);
NGramExtractor trigramExtractor = new NGramExtractor(3);
IEnumerable<string> sentencesA = segmenter.GetSentences(a);
IEnumerable<string> sentencesB = segmenter.GetSentences(b);
ITokenizer wordTokenizer = new ClassicTokenizer(false);
foreach (string sentenceB in sentencesB)
{
IList<string> wordsB = wordTokenizer.Tokenize(sentenceB);
ISet<NGram> wordTrigramsB = trigramExtractor.ExtractAsSet(wordsB);
bool foundMatchingSentence = false;
foreach (string sentenceA in sentencesA)
{
// This will be repeated for every sentence in B. It would be more efficient
// to generate trigrams for all sentences in A once, before we enter these loops:
IList<string> wordsA = wordTokenizer.Tokenize(sentenceA);
ISet<NGram> wordTrigramsA = trigramExtractor.ExtractAsSet(wordsA);
if (wordTrigramsA.Intersect(wordTrigramsB).Any())
{
// We found a sentence in A that shares word-trigrams, so stop comparing:
foundMatchingSentence = true;
break;
}
}
// No matching sentence in A? Then this sentence is unique to B:
if (!foundMatchingSentence)
yield return sentenceB;
}
}
Apparently segmenter also returns an extra, empty sentence, which you may want to filter out (or figure out how to prevent segmenter from doing that).
I'm sure the above code can be optimized if performance is a concern, but I'll leave that up to you.
My code is currently busted and I am aware of this. I'm working one one part at a time and I have hit two stumbling blocks which I'm hoping to find help here in regards too.
I am trying to address my formatting issue with this question here.
With the code below, i read in a text file using StreamReader, and the selections come in all on the same line as I want them too seperated by a BAR ("|"). Unfortunately, the bar prints at the end of each word, not actually 'between' each word. I don't want the last bar to print. I know i could cheat and add a bar to the font with a Console.Write("|") but that would be cheating in my mind - i wouldn't really be printing/displaying a bar between the words as I was trying to make a more elegant console selection.
This is just something I'm doing to learn and is not part of our assignment, but i like to push myself when I can. I am very very new to attempting to code, I am a complete newb, please assume I know nothing and please know all help is very much appreciated.
THIS IS MY CODE THUS FAR:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;// Needed for Streaming...
using System.IO;// Needed for Streaming...
namespace a090___StreamReader_DictionarySearch
{
class Program
{
private const String FILE_NAME = "dictionary.txt";//establish text file instance
public void Play()
{
do
{
DisplayTitle();
List<Listing> items = LoadListings();//create a list of WordDef objects
int count = 0;
foreach (Listing myListing in items)//read in items from file
{
//Console.WriteLine(myListing.GetName() + ": " + myListing.GetDefinition());
Console.Write(myListing.GetName());
if (count != items.Count - 1)
{
Console.Write(" | ");
}
count++;
}
DisplayText("\n\nPlease enter a word from the selections about to see it's definition");// Nice use of PROMPT
String userSelection = Console.ReadLine().ToLower();//Capture input
/// What are we trying to do?
/// Collect value entered for comparison - chek!
///
/// Compare value entered against list (Does value match to name?)
/// IF value matches print description
/// if value does not match(Else), state no match
//if (userSelection == Listing.name)
//{Console.WriteLine("You selected: " + userSelection() +
// "\nWhich Means: " + Listing.GetDefinition());}
//else{Console.WriteLine("I'm sorry, I don't have a match for that.");}
} while (PlayAgain());
Salutation();
}
//ToolBox -- my program specific tools
public List<Listing> LoadListings()//load entries display as list
{
StreamReader fileIn = new StreamReader(FILE_NAME);
List<Listing> entry = new List<Listing>();
//loop through every line of the file
while (!fileIn.EndOfStream)
{
String line = fileIn.ReadLine();
String[] pieces = line.Split(':');
if (pieces.Length < 1) continue;//error handling - set to length of text items
Listing myListing = new Listing(pieces[0], pieces[1]);
entry.Add(myListing);
}
fileIn.Close(); return entry;
}
//MaxBox -- my useful tools
public void DisplayText(String StringNameIs)
{ Console.WriteLine(StringNameIs); }//Where are we?
public Boolean PlayAgain()
{
Console.Write("\n\nDo you select again? (y)es or (n)o: ");
String command = Console.ReadLine().ToLower();
if (command == "y" || command == "yes") return true;
return false;
}
public void Salutation()
{ Console.Clear(); Console.WriteLine("Ti Do - oh - oh Ti Do -- So Do!"); } //The last line from the 'do-re-mi' song from the Sound of Music
public void DisplayTitle()
{ Console.Clear(); Console.WriteLine(">>>-- A Dictionary of Sounds --<<< \n"); } //Announce Our Program
static void Main(string[] args)
{
Program DictionaryLookup = new Program();
DictionaryLookup.Play();
Console.Read();
}
}
}
Please note: I have searched StackOverflow, GOOGLE, BING, MS' resources, et al. for an answer i can understand with my limited skills/understanding. I've also been working on this for a few hours. Please help.
You're looking for string.Join. Replace the whole foreach loop
foreach (Listing myListing in items)//read in items from file
{
//Console.WriteLine(myListing.GetName() + ": " + myListing.GetDefinition());
Console.Write(myListing.GetName() + " | ");
}
with
Console.Write(string.Join(" | ", items.Select(x => x.GetName())));
Alternatively, you could try a writing a backspace to the console.
Edit: As noted in the comments, you'd need to print a space char over the last pipe.
Console.Write("\b \b");
You may convert List of objects to array of object property and cast it to a string with delimeter:
string str = String.Join(" | ", items.Select(x => x.GetName()).ToArray());
try something like this...
var items = LoadListings();//create a list of WordDef objects
int i = 1;
var sw = new StringBuilder();
foreach(var myListing in items)
{
sw.AppendFormat("{0}", myListing.GetName() + "|");
if (i=items.Count)
{
sw.Replace("|", "", i,i);
}else
{ i++; }
}
//Console.WriteLine(myListing.GetName() + ": " + myListing.GetDefinition());
Console.Write(sw);
List<Listing> items = LoadListings(); //create a list of WordDef objects
int checklast = 0;
foreach (Listing myListing in items) //read in items from file
{
if (checklast == items.Count-1)
{
Console.Write(myListing.GetName());
}
else
{
//Console.WriteLine(myListing.GetName() + ": " + myListing.GetDefinition());
Console.Write(myListing.GetName() + " | ");
}
++checklast;
}
You can use the lazy method by checking when the last item will come so you make sure won't add BAR at the end.