How to return tvalues from dictionary C# - c#

I've got a dictionary, i need to return the tvalue based on the search from a string array.
How do I return the tvalue for the tkey matching the string i'm searching for and the tvalue for the entry directly after it (to calculate the length of the entry.... this is so I can then access the original file and import the data).
parameters = a string array to be found.
The dictionary (dict) is setup as tkey = name, tvalue = bytes from the start of the file.
input 2 is the file with all the info in it.
foreach (var p in parameters)
{
if (dict.ContainsKey(p))
{
int posstart = //tvalue of the parameter found;
int posfinish = //tvalue ofnext entry ;
using (FileStream fs = new FileStream(input[2], FileMode.Open, FileAccess.Read))
{
byte[] bytes = //posstart to pos finish
System.Console.WriteLine(Encoding.Default.GetString(bytes));
}
}
else
{
Console.WriteLine($"error, {p} not found");
}
}
Any help is welcomed, Thank you in advanced.

The key problem here is this comment:
// tvalue of next entry
In C# dictionaries are not ordered, so there is no "next entry". (A SortedDictionary sorts on key, not value, so that's no help to you. An OrderedDictionary might be what you want, but let's assume that you have a Dictionary in hand and solve the problem from that point.)
Let's transform your unsorted name -> offset data structure into a better data structure.
Suppose we start with this:
// name -> offset
var dict = new Dictionary<string, int>() {
{ "foo", 100 }, { "bar", 40 }, { "blah", 200 } };
We'll sort the dictionary by value and put the offsets into this sorted list:
// index -> offset
var list = new List<int>();
And then we'll make a new dictionary that maps names to indexes into this list:
// name -> index
var newDict = new Dictionary<string, int>();
Let's build the list and new dictionary from the old one:
foreach (var pair in dict.OrderBy(pair => pair.Value))
{
newDict[pair.Key] = list.Count;
list.Add(pair.Value);
}
We also need the offset of the last byte as the last thing in the list:
list.Add(theOffsetOfTheLastByte);
And now we can do a two-step lookup to get the offset and the next offset. First we look up the index by name, and then the offset by index:
int fooIndex = newDict["foo"]; // 1
int fooOffset = list[fooIndex]; // 100
int nextIndex = fooIndex + 1;
int nextOffset = list[nextIndex]; // 200
Make sense?

Related

C# Multiple Keys with Multiple Values in Dictionary and postprocess the Dictonary [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have a csv file with repetitive keys and multiple values from a csv. I have successfully parsed every row of csv in C#
My csv file looks like this
eid,hr
ABC,25
ABC,35
FDG,50
LMN,61
Task1
I would want to construct a dictionary
like
Dictionary<string,int[]> or Dictonary<string,List>
key1: ABC value1: 25
key2: ABC value2: 35
key3: FDG value3: 50
key4: LMN value4: 61
Task 2
Iterate through keys and make sure the total of the values for the same key is more than 50.
value1 and value2 will be proportioned to make the total as 50.
Here is the solution:
namespace CSVRead {class Program
{
static void Main(string[] args)
{
string[] csvLines = System.IO.File.ReadAllLines("//TheCSVfilepath");
var eID = new List<string>();
var hr = new List<double>();
for ( int i = 1; i < csvLines.Length; i++)
{
string[] rowData = csvLines[i].Split(',');
eID.Add(rowData[0]);
double hre = Convert.ToDouble(rowData[7]);
hr.Add(hre);
}
Console.WriteLine("eidDict: ");
for ( int m =0 ; m < eID.Count ; m++)
{
List<(string Key, double Value)> list = new List<(string Key, double Value)>; //CS1526Error
var eIDdictlist = list;
for (int n =0; n < hr.Count; n++)
{
employedictlist.Add(new KeyValuePair<string, double>(eID[m], hr[n])); //CS1503 Error
Console.WriteLine(eIDdictlist);
}
}
Console.ReadKey();
}
}
Your code doesn't seem to do remotely what your question is asking about. Here's what you need to do to get a dictionary of the list of values:
static void Main(string[] args)
{
var csvLines = File.ReadAllLines("//TheCSVfilepath");
var dictionary = new Dictionary<string, List<double>>();
foreach (var line in csvLines)
{
var (eID, hr) = GetEidAndHr(line);
if (!dictionary.TryGetValue(eID, out var hrList))
{
hrList = new List<double>();
dictionary[eID] = hrList;
}
hrList.Add(hr);
}
Console.WriteLine("eidDict: ");
//foreach (var keyValuePair in dictionary) // for pre c# 8
foreach (var (eid,hrList) in dictionary)
{
// var eid = keyValuePair.Key;// for pre c# 8
// var hrList = keyValuePair.Value;// for pre c# 8
var sum = hrList.Sum();
Console.WriteLine($"{eid}, {sum}");
}
}
static Tuple<string, double> GetEidAndHr(string csvLine)
{
var rowData = csvLine.Split(',');
return new Tuple<string, double>(rowData[0],double.Parse(rowData[7]));
}
If you are just trying to get your code to compile, here is what you need to change:
List<(string Key, double Value)> list = new List<(string Key, double Value)>; //CS1526Error
Needs to become
List<(string Key, double Value)> list = new List<(string Key, double Value)>();
And you also need to declare your variable employedictlist before you use it.
I think the following code should work for you:
// Test data to mock data from the CSV file
static IEnumerable<(string key, double value)> ReadCsv() =>
new[]
{
("ABC", 42.0),
("ABC", 123.0),
("DEF", 35.0),
("DEF", 15.0)
};
static void Main(string[] args)
{
// Task 1: Constructing a Dictionary<string, List<double>>
var data = ReadCsv()
.GroupBy(entry => entry.key)
.ToDictionary(
group => group.Key,
group => group.Select(entry => entry.value));
// Task 2: Iterating through the keys and verifying the total
var isSuccess = data.All(entry => entry.Value.Sum() > 50);
if (isSuccess)
Console.WriteLine("The total of values for each and every entry is greater than 50");
else
Console.WriteLine("The total of values for each and every entry is not greater than 50");
}
To read data from a CSV file, I would suggest using something like:
static IEnumerable<(string key, double value)> ReadCsv(string filePath, uint nHeaderLines = 0)
{
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (var reader = new StreamReader(stream))
{
var lineIndex = 0u;
while (reader.ReadLine() is string dataEntry)
{
if (lineIndex++ < nHeaderLines // Skip header
|| string.IsNullOrWhiteSpace(dataEntry)) // Ignore blank lines
{
continue;
}
var dataElements = dataEntry.Split(',', StringSplitOptions.RemoveEmptyEntries);
var key = dataElements[0];
var value = double.Parse(dataElements[7], NumberStyles.Float, NumberFormatInfo.InvariantInfo);
yield return (key, value);
}
}
}
Please note, that for the real solution it would also make sense to check the data line if it has all the entries, i.e., that elements at index 0 and 7 always exist and are valid

IDictionary<String, List<OpenXmlCompositeElement>> - get the List<OpenXmlCompositeElement>?

I'm working with Open XML & I have a IDictionary<String, List<OpenXmlCompositeElement>> structure. I want to work with the List part of the structure but this.map.Values tries to wrap it in an ICollection. How can I get the List part from my structure?
public List<OpenXmlCompositeElement> MapData()
{
//this does not work
return this.map.Values;
}
Since it is a dictionary, it expects you to tell from which key you want the value.
So this would be the code you need, where yourKey is the key you want to retrieve:
public List<OpenXmlCompositeElement> MapData()
{
return this.map["yourKey"];
}
If you have no interest in the key, and the dictionary is just a dictionary because the serializer says so, you could get the first item for example like this:
public List<OpenXmlCompositeElement> MapData()
{
return this.map.Values.First();
}
You can either loop through the dictionary and use the value you would like, or access the List directly using the key (in this case it's a string)
IDictionary<String, List<OpenXmlCompositeElement>> myDictionary;
List<OpenXmlCompositeElement> myList = myDictionary["myKey"];
Where myKey is available in the dictionary.
Alternatively you can loop through
foreach (var item in myDictionary)
{
var key = item.Key;
var value = item.Value
// You could then use `key` if you are unsure of what
// items are in the dictionary
}
Assuming this is your dictionary...
IDictionary<string, List<OpenXmlCompositeElement>> items = ...;
Get a specific List by key...
List<OpenXmlCompositeElement> list = items["key"];
Get the first list in the dictionary...
List<OpenXmlCompositeElement> list = items.Values.First();
Concatenate all lists in the dictionary into a single list...
List<OpenXmlCompositeElement> list = items.SelectMany(o => o).ToList();
foreach(KeyValuePair<string, List<OpenXmlCompositeElement>> kvp in IDictionary)
{
string key = kvp.key
List<OpenXmlCompositeElement> list = kvp.Value;
foreach(OpenXmlCompositeElement o in list)
{
Do anything you need to your List here
}
}
I am working with dictionaries as well, so here is a real example that I am currently working with:
foreach(KeyValuePair<string, List<DataRecords>> kvp in vSummaryResults)
{
string sKey = kvp.Key;
List<DataRecords> list = kvp.Value;
string[] vArr = sKey.Split(',');
int iTotalTradedQuant = 0;
double dAvgPrice = 0;
double dSumQuantPrice = 0;
double dQuantPrice = 0;
double dNumClose = 0;
foreach (DataRecords rec in list)
{
if(vSummaryResults.ContainsKey(sKey))
{
iTotalTradedQuant += rec.iQuantity;
dQuantPrice = rec.iQuantity * rec.dInputTradePrice;
dSumQuantPrice += dQuantPrice;
dAvgPrice = dSumQuantPrice / iTotalTradedQuant;
dNumClose = rec.dNumericClosingPrice;
}
else
{
vSummaryResults.Add(sKey, list);
//dNumClose = rec.dNumericClosingPrice;
}

Parse text file into dictionary C# <string> <int>

I have some code that is not working for parsing a text file to a dictionary...
Dictionary<string, int> dictDSDRecordsByValidCompCode = new Dictionary<string, int>(); // This dictionary will get rid of pipe delimited comp codes, get distinct, and keep cuont of how many DSD records per Comp Code
if (LineToStartOn.pInt > 0)
{
using (var sr = new StreamReader("\\rvafiler1\rdc\clients\sams\pif\DSD_Dictionary.txt"))
{
string line = null;
string key = null;
int value = 0;
// while it reads a key
while ((line = sr.ReadLine()) != null)
{
// add the key and whatever it
// can read next as the value
dictDSDRecordsByValidCompCode.Add(key, sr.ReadBlock);
dictDSDRecordsByValidCompCode.Add(value, sr.ReadBlock());
}
}
}
The last line is where it fails. It does not like the dictionay.Add(Line, sr.ReadBlock()) statements. Where am I going wrong?
I need to read in a string followed by an int,.
Your dictionary is declared as <string, int> but the second value you are adding is another string (from sr.ReadLine) I think you want a dictionary of <string, string>
Probably you want to make it as follows:
Where your key is line number
and your string is your line;
var dictDSDRecordsByValidCompCode = new Dictionary<int, string>(); // This dictionary will get rid of pipe delimited comp codes, get distinct, and keep cuont of how many DSD records per Comp Code
if (LineToStartOn.pInt > 0)
{
using (var sr = new StreamReader("\\rvafiler1\rdc\clients\sams\pif\DSD_Dictionary.txt"))
{
string line = null;
int lineNumber = 1;
// while it reads a key
while (!string.IsNullOrEmpty(line = sr.ReadLine()) )
{
// add the key and whatever it
// can read next as the value
dictDSDRecordsByValidCompCode.Add(lineNumber++, line);
}
}
}
I think this is what you're trying to do.
Using StreamReader to count duplicates?
Dictionary<string, int> firstNames = new Dictionary<string, int>();
foreach (string name in YourListWithNames)
{
if (!firstNames.ContainsKey(name))
firstNames.Add(name, 1);
else
firstNames[name] += 1;
}
If you are trying to add a line as a key and subsequent number from file as a value, it should look like this:
string key = null;
int value = 0;
// while it reads a key
while ((key = sr.ReadLine()) != null)
{
//read subsequent value
value = Convert.ToInt32(sr.ReadLine());
//put a key/value pair to dictionary
dictDSDRecordsByValidCompCode.Add(key, value);
}

Grouping by an unknown initial prefix

Say I have the following array of strings as an input:
foo-139875913
foo-aeuefhaiu
foo-95hw9ghes
barbazabejgoiagjaegioea
barbaz8gs98ghsgh9es8h
9a8efa098fea0
barbaza98fyae9fghaefag
bazfa90eufa0e9u
bazgeajga8ugae89u
bazguea9guae
aifeaufhiuafhe
There are 3 different prefixes used here, "foo-", "barbaz" and "baz" - however these prefixes are not known ahead of time (they could be something completely different).
How could you establish what the different common prefixes are so that they could then be grouped by? This is made a bit tricky since in the data I've provided there's two that start with "bazg" and one that starts "bazf" where of course "baz" is the prefix.
What I've tried so far is sorting them into alphabetical order, and then looping through them in order and counting how many characters in a row are identical to the previous. If the number is different or when 0 characters are identical, it starts a new group. The problem with this is it falls over at the "bazg" and "bazf" problem I mentioned earlier and separates those into two different groups (one with just one element in it)
Edit: Alright, let's throw a few more rules in:
Longer potential groups should generally be preferred over shorter ones, unless there is a closely matching group of less than X characters difference in length. (So where X is 2, baz would be preferred over bazg)
A group must have at least Y elements in it or not be a group at all
It's okay to simply throw away elements that don't match any of the 'groups' to within the rules above.
To clarify the first rule in relation to the second, if X was 0 and Y was 2, then the two 'bazg' entries would be in a group, and the 'bazf' would be thrown away because its on its own.
Well, here's a quick hack, probably O(something_bad):
IEnumerable<Tuple<String, IEnumerable<string>>> GuessGroups(IEnumerable<string> source, int minNameLength=0, int minGroupSize=1)
{
// TODO: error checking
return InnerGuessGroups(new Stack<string>(source.OrderByDescending(x => x)), minNameLength, minGroupSize);
}
IEnumerable<Tuple<String, IEnumerable<string>>> InnerGuessGroups(Stack<string> source, int minNameLength, int minGroupSize)
{
if(source.Any())
{
var tuple = ExtractTuple(GetBestGroup(source, minNameLength), source);
if (tuple.Item2.Count() >= minGroupSize)
yield return tuple;
foreach (var element in GuessGroups(source, minNameLength, minGroupSize))
yield return element;
}
}
Tuple<String, IEnumerable<string>> ExtractTuple(string prefix, Stack<string> source)
{
return Tuple.Create(prefix, PopWithPrefix(prefix, source).ToList().AsEnumerable());
}
IEnumerable<string> PopWithPrefix(string prefix, Stack<string> source)
{
while (source.Any() && source.Peek().StartsWith(prefix))
yield return source.Pop();
}
string GetBestGroup(IEnumerable<string> source, int minNameLength)
{
var s = new Stack<string>(source);
var counter = new DictionaryWithDefault<string, int>(0);
while(s.Any())
{
var g = GetCommonPrefix(s);
if(!string.IsNullOrEmpty(g) && g.Length >= minNameLength)
counter[g]++;
s.Pop();
}
return counter.OrderBy(c => c.Value).Last().Key;
}
string GetCommonPrefix(IEnumerable<string> coll)
{
return (from len in Enumerable.Range(0, coll.Min(s => s.Length)).Reverse()
let possibleMatch = coll.First().Substring(0, len)
where coll.All(f => f.StartsWith(possibleMatch))
select possibleMatch).FirstOrDefault();
}
public class DictionaryWithDefault<TKey, TValue> : Dictionary<TKey, TValue>
{
TValue _default;
public TValue DefaultValue {
get { return _default; }
set { _default = value; }
}
public DictionaryWithDefault() : base() { }
public DictionaryWithDefault(TValue defaultValue) : base() {
_default = defaultValue;
}
public new TValue this[TKey key]
{
get { return base.ContainsKey(key) ? base[key] : _default; }
set { base[key] = value; }
}
}
Example usage:
string[] input = {
"foo-139875913",
"foo-aeuefhaiu",
"foo-95hw9ghes",
"barbazabejgoiagjaegioea",
"barbaz8gs98ghsgh9es8h",
"barbaza98fyae9fghaefag",
"bazfa90eufa0e9u",
"bazgeajga8ugae89u",
"bazguea9guae",
"9a8efa098fea0",
"aifeaufhiuafhe"
};
GuessGroups(input, 3, 2).Dump();
Ok, well as discussed, the problem wasn't initially well defined, but here is how I'd go about it.
Create a tree T
Parse the list, for each element:
for each letter in that element
if a branch labeled with that letter exists then
Increment the counter on that branch
Descend that branch
else
Create a branch labelled with that letter
Set its counter to 1
Descend that branch
This gives you a tree where each of the leaves represents a word in your input. Each of the non-leaf nodes has a counter representing how many leaves are (eventually) attached to that node. Now you need a formula to weight the length of the prefix (the depth of the node) against the size of the prefix group. For now:
S = (a * d) + (b * q) // d = depth, q = quantity, a, b coefficients you'll tweak to get desired behaviour
So now you can iterate over each of the non-leaf node and assign them a score S. Then, to work out your groups you would
For each non-leaf node
Assign score S
Insertion sort the node in to a list, so the head is the highest scoring node
Starting at the root of the tree, traverse the nodes
If the node is the highest scoring node in the list
Mark it as a prefix
Remove all nodes from the list that are a descendant of it
Pop itself off the front of the list
Return up the tree
This should give you a list of prefixes. The last part feels like some clever data structures or algorithms could speed it up (the last part of removing all the children feels particularly weak, but if you input size is small, I guess speed isn't too important).
I'm wondering if your requirements aren't off. It seems as if you are looking for a specific grouping size as opposed to specific key size requirements. I have below a program that will, based on a specified group size, break up the strings into the largest possible groups up too, and including the group size specified. So if you specify a group size of 5, then it will group items on the smallest key possible to make a group of size 5. In your example it would group foo- as f since there is no need to make a more complex key as an identifier.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
/// <remarks><c>true</c> in returned dictionary key are groups over <paramref name="maxGroupSize"/></remarks>
public static Dictionary<bool,Dictionary<string, List<string>>> Split(int maxGroupSize, int keySize, IEnumerable<string> items)
{
var smallItems = from item in items
where item.Length < keySize
select item;
var largeItems = from item in items
where keySize < item.Length
select item;
var largeItemsq = (from item in largeItems
let key = item.Substring(0, keySize)
group item by key into x
select new { Key = x.Key, Items = x.ToList() } into aGrouping
group aGrouping by aGrouping.Items.Count() > maxGroupSize into x2
select x2).ToDictionary(a => a.Key, a => a.ToDictionary(a_ => a_.Key, a_ => a_.Items));
if (smallItems.Any())
{
var smallestLength = items.Aggregate(int.MaxValue, (acc, item) => Math.Min(acc, item.Length));
var smallItemsq = (from item in smallItems
let key = item.Substring(0, smallestLength)
group item by key into x
select new { Key = x.Key, Items = x.ToList() } into aGrouping
group aGrouping by aGrouping.Items.Count() > maxGroupSize into x2
select x2).ToDictionary(a => a.Key, a => a.ToDictionary(a_ => a_.Key, a_ => a_.Items));
return Combine(smallItemsq, largeItemsq);
}
return largeItemsq;
}
static Dictionary<bool, Dictionary<string,List<string>>> Combine(Dictionary<bool, Dictionary<string,List<string>>> a, Dictionary<bool, Dictionary<string,List<string>>> b) {
var x = new Dictionary<bool,Dictionary<string,List<string>>> {
{ true, null },
{ false, null }
};
foreach(var condition in new bool[] { true, false }) {
var hasA = a.ContainsKey(condition);
var hasB = b.ContainsKey(condition);
x[condition] = hasA && hasB ? a[condition].Concat(b[condition]).ToDictionary(c => c.Key, c => c.Value)
: hasA ? a[condition]
: hasB ? b[condition]
: new Dictionary<string, List<string>>();
}
return x;
}
public static Dictionary<string, List<string>> Group(int maxGroupSize, IEnumerable<string> items, int keySize)
{
var toReturn = new Dictionary<string, List<string>>();
var both = Split(maxGroupSize, keySize, items);
if (both.ContainsKey(false))
foreach (var key in both[false].Keys)
toReturn.Add(key, both[false][key]);
if (both.ContainsKey(true))
{
var keySize_ = keySize + 1;
var xs = from needsFix in both[true]
select needsFix;
foreach (var x in xs)
{
var fixedGroup = Group(maxGroupSize, x.Value, keySize_);
toReturn = toReturn.Concat(fixedGroup).ToDictionary(a => a.Key, a => a.Value);
}
}
return toReturn;
}
static Random rand = new Random(unchecked((int)DateTime.Now.Ticks));
const string allowedChars = "aaabbbbccccc"; // "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
static readonly int maxAllowed = allowedChars.Length - 1;
static IEnumerable<string> GenerateText()
{
var list = new List<string>();
for (int i = 0; i < 100; i++)
{
var stringLength = rand.Next(3,25);
var chars = new List<char>(stringLength);
for (int j = stringLength; j > 0; j--)
chars.Add(allowedChars[rand.Next(0, maxAllowed)]);
var newString = chars.Aggregate(new StringBuilder(), (acc, item) => acc.Append(item)).ToString();
list.Add(newString);
}
return list;
}
static void Main(string[] args)
{
// runs 1000 times over autogenerated groups of sample text.
for (int i = 0; i < 1000; i++)
{
var s = GenerateText();
Go(s);
}
Console.WriteLine();
Console.WriteLine("DONE");
Console.ReadLine();
}
static void Go(IEnumerable<string> items)
{
var dict = Group(3, items, 1);
foreach (var key in dict.Keys)
{
Console.WriteLine(key);
foreach (var item in dict[key])
Console.WriteLine("\t{0}", item);
}
}
}
}

How to randomly remove a key from dictionary based on value?

How can I randomly remove a key with value 0 efficiently ?
Dictionary<string, int> dict = new Dictionary<Edge, int>();
dict.add("a",0);
dict.add("b",0);
dict.add("c",0);
dict.add("d",1);
The size of dictionary is 10000.
Something like this should do it:
IEnumerable<string, int> pairsToRemove = dictionary.Where(pair => pair.Value == 0);
To generate a random index, you could use:
int indexToRemove = [RandomNumber] % pairsToRemove.Length() -1;
Find the indexToRemove th element from pairsToRemove and remove it from the dictionary.
As to efficiency:
The complexity should be O(n)[get all items with value 0] + O(.6N)[finding ith value to remove] + O(log(n))[deletion] assuming the random number generation is constant time.
The problem is, there is no way to perform a value lookup on a dictionary in better than O(n) time. So that will be your bottleneck.
This will remove the first item with a zero value. It's not precisely "random", but is non-deterministic.
Dictionary<string, int> dict = new Dictionary<string, int>();
string keyToRemove = null;
foreach (var kvp in dict)
{
if (kvp.Value == 0)
{
keyToRemove = kvp.Key;
break;
}
}
if (keyToRemove != null)
{
dict.Remove(keyToRemove);
}

Categories