I'm reading a csv file that contains abbreviations and the full version for example LOL,Laughing out Loud. I've created a dictionary where the abbreviation is the key and full version is the value.
'''
private void btnFilter_Click(object sender, RoutedEventArgs e)
{
var keys = new List<string>();
var values = new List<string>();
using (var rd = new StreamReader("textwords.csv"))
{
while (!rd.EndOfStream)
{
var splits = rd.ReadLine().Split(',');
keys.Add(splits[0]);
values.Add(splits[1]);
}
}
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v }).ToDictionary(x => x.Key, x => x.Value);
foreach (var k in dictionary)
{
//
}
aMessage.MessageContent = txtContent.Text;
}
'''
I'm using a button that will check if the textbox txtContent contains any of the abbreviations in the text and change this to the full version. So if the textbox contained the following. "Going to be AFK" after the button click it would change this to "Going to be away from keyboard".
I want to write a foreach loop that would check for any abbreviations, elongate them and then save this to a string variable MessageContent.
Would anyone be able to show me the best way to go about this as I'm not sure how to do this with the input from a textbox?
Thanks
You can just use LINQ to read and create a dictionary object:
var dictionary = File.ReadAllLines(#"textwords.csv")
.Select(x => x.Split(",",StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(key => key.FirstOrDefault().Trim(),
value => value.Skip(1).FirstOrDefault().Trim());
If the abbrevs are correct, i.e. you don't need fuzzy logic, you can use a variety of .NET objects and search the keys rather quickly.
if(dict.ContainsKey(myKey))
{
}
I did that freehand, so it might be dict.Keys.Contains() or similar. The point is you can search the dictionary directly rather than loop.
If you need to do a more fuzzy search, you can utilize LINQ to write queries to iterate over collections of objects extremely fast (and few lines).
As for yours, you would iterate over dictionary.Keys to seach keys, but I still don't see why you need the extra code.
Related
I have a dictionary and I want to retrieve all the values list from the dictionary based on a condition on the key, i.e. I want to retrieve only the values for which the respective key matches in alist.
Example: dictionary is as follows
IDictionary<string, string> maskingValues = new Dictionary<string, string>();
maskingValues.Add("cat", "Me#ena");
maskingValues.Add("dog", "N&avya");
maskingValues.Add("llama", "vivek!a");
maskingValues.Add("iguana", "sh^ams");
and I have list of strings as
List<string> keysString = new List<string>();
keysString.Add("cat");
keysString.Add("fox");
Now my requirement is to get the values list from the dictionary where the key matches from the keysString list.
The output should be
Me#ena
till now what I have done is
var all_parameters = maskingValues .Keys.ToList();
var parameters_to_mask = all_parameters.Intersect(keysString);
var values_list = parameters_to_mask.Where(k => data_dictionary.ContainsKey(k)).Select(k => data_dictionary[k]);
so values_list will contain the output Me#ena, I retrieved all the keys from the dictionary and then compared the keys list with the keysString list and then retrieved the values list from the output of the two list's intersect. But can I do it in more optimized way so that the code and the performance is good.
Thanks in advance. Please help me out.
This should work:
var q = maskingValues.Where(x => keysString.Contains(x.Key)).Select(x => x.Value);
foreach (var item in q)
{
Console.WriteLine(item);
}
There are a lot of solutions. This one for example:
var newlist = maskingValues.Where(x => keysString.Contains(x.Key)).Select(y=>y.Value).ToList();
I came up with a quick bit of code to do this fully using linq.
keysString.Where(x => maskingValues.Keys.Contains(x)).ToList().ForEach(x => Console.WriteLine(maskingValues[x]));
Not sure I got the spec right but this is faster than linq:
var matches = new List<string>();
foreach (var key in keysString)
{
if (maskingValues.TryGetValue(key, out string value))
{
matches.Add(value);
}
}
If your dictionary is large, you can improve performance by taking advantage of the fact that accessing an element by key in a dictionary is O(
var result = keysString
.Select(k =>
{ string value;
maskingValues.TryGetValue(k, out value);
return value;
})
.Where(v => v != null);
... etc ...
Note that using TryGetValue is more efficient than calling Contains then the dictionary indexer.
This Should Work:
Below Solution is used when you know your Key name and want to retrive value of key
string objectValue;
maskingValues.TryGetValue("cat", out objectValue);
If you want to Retrive All values from Dictionary than used single line of code: maskingValues.Values
So I tried some research, but I just don't know how to google this..
For example, I got a .db (works same as .txt for me) file, written like this:
DIRT: 3;
STONE: 6;
so far, i got a code that can put items in a comboBox like this:
DIRT,
STONE,
will put DIRT and STONE in the comboBox. This is the code I'm using for that:
string[] lineOfContents = System.IO.File.ReadAllLines(dbfile);
foreach (var line in lineOfContents)
{
string[] tokens = line.Split(',');
comboBox1.Items.Add(tokens[0]);
}
How do I expand this so it put e.g. DIRT and STONE in the combobox, and keep the rest (3) in variables (ints, like int varDIRT = 3)?
If you want, it doesn't have to be txt or db files.. i heard xml are config files too.
Try doing something like this:
cmb.DataSource = File.ReadAllLines("filePath").Select(d => new
{
Name = d.Split(',').First(),
Value = Convert.ToInt32(d.Split(',').Last().Replace(";",""))
}).ToList();
cmb.DisplayMember = "Name";
cmb.ValueMember= "Value";
remember it will require to use using System.Linq;
if your want ot reference the selected value of the combobox you can use
cmb.SelectedValue;
cmb.SelectedText;
I think you've really got two questions, so I'll try to answer them separately.
The first question is "How can I parse a file that looks like this...
DIRT: 3;
STONE: 6;
into names and integers?" You could remove all the whitespace and semicolons from each line, and then split on colon. A cleaner way, in my opinion, would be to use a regular expression:
// load your file
var fileLines = new[]
{
"DIRT: 3;",
"STONE: 6;"
};
// This regular expression will match anything that
// begins with some letters, then has a colon followed
// by optional whitespace ending in a number and a semicolon.
var regex = new Regex(#"(\w+):\s*([0-9])+;", RegexOptions.Compiled);
foreach (var line in fileLines)
{
// Puts the tokens into an array.
// The zeroth token will be the entire matching string.
// Further tokens will be the contents of the parentheses in the expression.
var tokens = regex.Match(line).Groups;
// This is the name from the line, i.e "DIRT" or "STONE"
var name = tokens[1].Value;
// This is the numerical value from the same line.
var value = int.Parse(tokens[2].Value);
}
If you're not familiar with regular expressions, I encourage you to check them out; they make it very easy to format strings and pull out values. http://regexone.com/
The second question, "how do I store the value alongside the name?", I'm not sure I fully understand. If what you want to do is back each item with the numerical value specified in the file, the dub stylee's advice is good for you. You'll need to place the name as the display member and value as the value member. However, since your data is not in a table, you'll have to put the data somewhere accessible so that the Properties you want to use can be named. I recommend a dictionary:
// This is your ComboBox.
var comboBox = new ComboBox();
// load your file
var fileLines = new[]
{
"DIRT: 3;",
"STONE: 6;"
};
// This regular expression will match anything that
// begins with some letters, then has a colon followed
// by optional whitespace ending in a number and a semicolon.
var regex = new Regex(#"(\w+):\s*([0-9])+;", RegexOptions.Compiled);
// This does the same as the foreach loop did, but it puts the results into a dictionary.
var dictionary = fileLines.Select(line => regex.Match(line).Groups)
.ToDictionary(tokens => tokens[1].Value, tokens => int.Parse(tokens[2].Value));
// When you enumerate a dictionary, you get the entries as KeyValuePair objects.
foreach (var kvp in dictionary) comboBox.Items.Add(kvp);
// DisplayMember and ValueMember need to be set to
// the names of usable properties on the item type.
// KeyValue pair has "Key" and "Value" properties.
comboBox.DisplayMember = "Key";
comboBox.ValueMember = "Value";
In this version, I have used Linq to construct the dictionary. If you don't like the Linq syntax, you can use a loop instead:
var dictionary = new Dictionary<string, int>();
foreach (var line in fileLines)
{
var tokens = regex.Match(line).Groups;
dictionary.Add(tokens[1].Value, int.Parse(tokens[2].Value));
}
You could also use FileHelpers library. First define your data record.
[DelimitedRecord(":")]
public class Record
{
public string Name;
[FieldTrim(TrimMode.Right,';')]
public int Value;
}
Then you read in your data like so:
FileHelperEngine engine = new FileHelperEngine(typeof(Record));
//Read from file
Record[] res = engine.ReadFile("FileIn.txt") as Record[];
// write to file
engine.WriteFile("FileOut.txt", res);
I am fairly new to C#
I am trying to retrieve some information from an external data source and store it in array, once it is in an array I wish to sort it by time.
I know how to do this for just one column in a row, however the information I require has multiple columns.
For example:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
// Sort my array by Appoint.Start
foreach ( item in myNewArray )
{
//print out Appoint.Subject - Appoint.Start, Appoint.Organiser.Name.ToString() and Appoint.location
}
Many thanks for your help.
EDIT:
I have multiple data sources which pull in this:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
Hence the need to sort the items in a new array, I know this isn't very efficent but there is no way of getting the information I need in any other way.
You can sort a list using the LINQ sorting operators OrderBy and ThenBy, as shown below.
using System.Linq;
and then...
var appointments = new List<Appointment>();
var sortedAppointments = list.OrderBy(l => l.Subject).ThenBy(l => l.Name).ToList();
This will create a new list of appointments, sorted by subject and then by name.
It's unclear what your final aim is but:
Use a generic List instead of an array:
See this SO question for more information as to why using a List is prefered.
List<Appointment> appointments = new List<Appointment>();
foreach (Appointment Appoint in fapts)
{
appointments.Add(Appoint);
}
foreach (var item in appointments)
{
Console.WriteLine(item.Subject);
Console.WriteLine(item.Foo);
// Here you could override ToString() on Appointment to print eveything in one Console.WriteLine
}
If the aim of your code is to order by time, try the following:
var sortedAppointments = fapts.OrderBy(a => a.Start); // assuming Start is a DateTime property of `Appointment`.
Consider a Dictionary Object instead of an array if the data is conceptually one row multiple columns.
foreach(KeyValuePair<string, string> entry in MyDic)
{
// do something with entry.Value or entry.Key
}
You already have a list of objects in fpts, sort that list itself:
fpts.OrderBy(x => x.Subject).ThenBy(x => x.Location).ToList();
LINQ is your friend here.
fapts appears to already be a collection so you could just operate on it.
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start).ToArray()
I've used the ToArray() call to force immediate evaluation and means that myNewArray is already sorted so that if you use it more than once you don't have to re-evaluate the sort.
Alternatively if you are only using this once you can just as easily miss the ToArray() portion out and then execution of the sort will be deferred until you try and enumerate through myNewArray.
This solution puts the source objects into the array, but if you are just wanting to store the specific fields you mention then you will need to use a select. You have two choices for the array item type, you can either use an anonymous class which provides difficulties if you are returning this array from a function or define a class.
For anonymous:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
For named class assuming class is MyClass:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new MyClass {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
You have a wide range of options. The 2 most common are:
1) Create a class, then define an array or list of that class, and populate that
2) Create a structure that matches the data format and create an array or list of that
Of course, you could put the data into an XML format or dataset, but that's probably more work than you need.
public List<foo> appointments = new List<foo>();
public struct foo
{
public string subject ;
public DateTime start ;
public string name ;
public string location ;
}
public void foo1()
{
// parse the file
while (!File.eof())
{
// Read the next line...
var myRecord = new foo() ;
myRecord.subject = data.subject ;
myRecord.start = data.Start ;
myRecord.name = data.Name ;
//...
appointments.Add(myRecord);
}
}
Enjoy
(Since I can't comment and reply to the comment - it wasn't clear if he had a class, etc. or was just showing us what he wanted to do. I assumed it was just for demonstration purposes since there wasn't any info as to how the data was being read. If he could already put it into a class, than the first answer applied anyway. I just tossed the last 2 in there because they were options for getting the data first.)
I'm new to ASP.NET C#. Trying to create an ArrayList with 2 columns one for the value (string) and one for counting how many of each. While adding values I need to search the ArrayList to find if the value already exist, if so add 1, if not, add it to the array and set count column to 1. Can someone provide a bit of code sample? If there is a better approach then I'd like to hear it.
private static Dictionary<string, int> values = new Dictionary<string, int>();
private static void Add(string newValue)
{
if(values.ContainsKey(newValue))
{
values[newValue]++; // Increment count of existing item
}
else
{
values.Add(newValue, 1); // Add new item with count 1
}
}
If you're just starting with a list of strings, there are plenty of simpler ways to do this.
I'd probably use the GroupBy extension here
List<string> items = GetItems(); // from somewhere
var groups = items.GroupBy(i => i);
var countedItems = groups.Select(g => new
{ Value = g.First(), HowMany = g.Count() });
Then putting into an ArrayList, if you want:
var arrayList = new ArrayList();
foreach (var thing in countedItems)
{
arrayList.Add(thing.Value + " " thing.HowMany);
}
But I'd probably prefer to put this into a Dictionary, because you know that each word will map to just one value - the number of times it appears.
var result = countedItems.ToDictionary(i => i.Value, i => i.HowMany);
Working on a application which reads some values from an customers.xml file to UI.
Linq to xml code:
var Settings = (from e in customer.Descendants("appSettings")
from kvpair in e.Elements("Name")
select new
{
Name = kvpair.Attribute("Zip").Value,
Node = kvpair
}).ToDictionary(x => x.Name, y => y);
txtFName.Text==Settings["CustomerA"].Node.attribute("value").Value;
txtLName=Settings["CustomerB"].Node.attribute("value").Value;
I am able to get the values into GUI from XMl file by the above code.
My question is when i comment out any element or data from xml file of a particular customer i get the error "the given key is not present in the dictionary"
How do i dynamically check whether a key exists in dictionary if then only read that value or else got to the next value ?
You're looking for the ContainsKey() or TryGetValue() methods.
The 'proper' way to check whether a key is present in a Dictionary is to use the ContainsKey function. dictionary.ContainsKey(keyValue);
However, you may want to ask yourself why the key isn't there and whether it should be there.
if (Settings.ContainsKey("CustomerA"))
{
txtLName.Text = Settings["CustomerA"].Node.attribute("value").Value;
}
EDIT:
You can loop through keys accessing Keys property collection:
foreach (var key in Settings.Keys)
{
// ..
}
Also you can use LINQ to filter out what you need:
IList<string> filteredKeys = new List<string> { "A", "B" };
Settings.Where(kv => filteredKeys.Contains(kv.Key));