How to separate the string and value to dictionary? - c#

I have a comma separated string with values as follows:
"File_name,cost_per_page,0.23,color_code,343,thickness,0.01".
I want to read cost_per_page = 0.23, color_code=343 and thickness=0.01.
How can I do this? I could do this by putting it to a list and reading successive element next to key string. Is there any other method?

The simplest (and therefor, probably the best) approach would be to simply use string.Split and then iterate the array:
var source = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
var splitted = source.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries)
var result = new Dictionary<string, string>();
// Note: Starting from 1 to skip the "file_name"
// moving 2 indexes in each iteration,
// and ending at length - 2.
for(int i = 1; i < splitted.Length - 1; i+=2)
{
result.Add(splitted[i], splitted[i+1]);
}
I've tried to find a clever way to do it with linq, but the best I came up with is really not that clever at all:
var valuesWithIndexes = source
.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries)
.Skip(1)
.Select((v, i) => new {v, i});
var keys = valuesWithIndexes.Where(x => x.i % 2 == 0);
var values = valuesWithIndexes.Where(x => x.i % 2 == 1);
var dictionary = keys.Zip(values, (k, v) => new {k, v})
.ToDictionary(key => key.k,
val => val.v);
I think the simple for loop is a clear winner in this case.

I think that this is the simplest and best approach.
string str = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
string[] array = str.Split(',');
Dictionary<string, double> dict = new Dictionary<string, double>();
for (int i = 1; i < array.Length - 1; i = i + 2)
{
string key = array[i];
double value = Double.Parse(array[i + 1], CultureInfo.InvariantCulture);
dict.Add(key, value);
}
You can also use the above code for a larger string (variable str has more key-value pairs).

I'd use linq.
Convert the string into a delimited array.
Set up a dictionary containing the first item...not sure if you want to do this. If not just remove the "d1" and "union" statements.
Run some Linq to create your dictionary based on even numbers.
Then if you really want to account for the first item, file_name, and you want that at the beginning of your dictionary then lastly you'd run the "union" statement.
string str = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
string[] array = str.Split(',');
Dictionary<string, string> d1 = new Dictionary<string, string>()
{
{ array[0], "" }
};
Dictionary<string, string> d2 = array.Select((i, index) => (index > 0 && index % 2 == 0) ?
new { key = array[index - 1], value = i } : null).Where(i => i != null).ToDictionary(d => d.key, d => d.value);
Dictionary<string, string> result = d1.Union(d2).ToDictionary(k => k.Key, v => v.Value);
foreach (KeyValuePair<string, string> kvp in result)
Console.WriteLine(kvp.Key + ": " + kvp.Value);
return;
another, and probably cleaner approach:
This accounts for File_name as well. If you don't want it then add "index > 0" to the conditional operator where it checks for even.
string str = "File_name,cost_per_page,0.23,color_code,343,thickness,0.01";
string[] array = str.Split(',');
Dictionary<string, string> d2 = array.Select((i, index) => (index % 2 == 0) ?
new
{
key = (index == 0) ? i : array[index - 1],
value = (index == 0) ? "" : i
} : null).Where(i => i != null).ToDictionary(d => d.key, d => d.value);
foreach (KeyValuePair<string, string> kvp in d2)
Console.WriteLine(kvp.Key + ": " + kvp.Value);

Full working example in this fiddle.
All you need is to iterate while you haven't reached your string array end ensuring your index increments by 2 (field name, field value).
int i = 1;
var temp = s.Split(',').ToList();
while(i < temp.Count) {
i = i+2;
}

Related

How to convert an array to a dictionary using LINQ

I have a string array that I want to convert to a Dictionary using Linq.
I want elements with a even index (including zero) to be keys and elements with an odd index to be values in the dictionary. I created a dictionary using a for loop:
string[] arr = new string[4];
arr[0] = "John";
arr[1] = "A";
arr[2] = "Luke";
arr[3] = "B";
Dictionary<string, string> myDict = new Dictionary<string, string>();
for (int i = 0; i < arr.Length - 1; i += 2)
{
myDict.Add(arr[i], arr[i + 1]);
}
//myDict -> { { "John", "A" },{"Luke","B"} }
And now I am curious how to do it with LINQ ToDictionary():
myDict = arr.ToDictionary();
You can put it like this (in case of Linq we can exploit Enumerable.Range as a loop):
string[] arr = new string[] {
"John", "A",
"Luke", "B",
}
var myDict = Enumerable
.Range(0, arr.Length / 2)
.ToDictionary(i => arr[2 * i],
i => arr[2 * i + 1]);
Console.WriteLine(string.Join(Environment.NewLine, myDict));
Outcome:
[John, A]
[Luke, B]
You can group by index divided by 2. Try this code:
string[] arr = new string[4];
arr[0] = "John";
arr[1] = "A";
arr[2] = "Luke";
arr[3] = "B";
var dict = arr.Select((s, i) => new {s, i})
.GroupBy(x => x.i / 2)
.ToDictionary(g => g.First().s, g => g.Last().s);

Remove the repeating items and return the order number

I want to remove the repeating items of a list.I can realize it whit Distinct() easily.But i also need to get the order number of the items which have been removed.I can't find any function in linq to solve the problem and finally realize it with the following code:
public List<string> Repeat(List<string> str)
{
var Dlist = str.Distinct();
List<string> repeat = new List<string>();
foreach (string aa in Dlist)
{
int num = 0;
string re = "";
for (int i = 1; i <= str.LongCount(); i++)
{
if (aa == str[i - 1])
{
num = num + 1;
re = re + " - " + i;
}
}
if (num > 1)
{
repeat.Add(re.Substring(3));
}
}
return repeat;
}
Is there any other way to solve the problem more simple? Or is there any function in linq I missed?Any advice will be appreciated.
This query does exactly the same as your function, if I'm not mistaken:
var repeated = str.GroupBy(s => s).Where(group => group.Any())
.Select(group =>
{
var indices = Enumerable.Range(1, str.Count).Where(i => str[i-1] == group.Key).ToList();
return string.Join(" - ", group.Select((s, i) => indices[i]));
});
It firstly groups the items of the original list, so that every item with the same content is in a group. Then it searches for all indices of the items in the group in the original list, so that we have all the indices of the original items of the group. Then it joins the indices to a string, so that the resulting format is similiar to the one you requested. You could also transform this statement lambda to an anonymous lambda:
var repeated = str.GroupBy(s => s).Where(group => group.Any())
.Select(group => string.Join(" - ",
group.Select((s, i) =>
Enumerable.Range(1, str.Count).Where(i2 => str[i2 - 1] == group.Key).ToList()[i])));
However, this significantly reduces performance.
I tested this with the following code:
public static void Main()
{
var str = new List<string>
{
"bla",
"bla",
"baum",
"baum",
"nudel",
"baum",
};
var copy = new List<string>(str);
var repeated = str.GroupBy(s => s).Where(group => group.Any())
.Select(group => string.Join(" - ",
group.Select((s, i) =>
Enumerable.Range(1, str.Count).Where(i2 => str[i2 - 1] == group.Key).ToList()[i])));
var repeated2 = Repeat(str);
var repeated3 = str.GroupBy(s => s).Where(group => group.Any())
.Select(group =>
{
var indices = Enumerable.Range(1, str.Count).Where(i => str[i-1] == group.Key).ToList();
return string.Join(" - ", group.Select((s, i) => indices[i]));
});
Console.WriteLine(string.Join("\n", repeated) + "\n");
Console.WriteLine(string.Join("\n", repeated2) + "\n");
Console.WriteLine(string.Join("\n", repeated3));
Console.ReadLine();
}
public static List<string> Repeat(List<string> str)
{
var distinctItems = str.Distinct();
var repeat = new List<string>();
foreach (var item in distinctItems)
{
var added = false;
var reItem = "";
for (var index = 0; index < str.LongCount(); index++)
{
if (item != str[index])
continue;
added = true;
reItem += " - " + (index + 1);
}
if (added)
repeat.Add(reItem.Substring(3));
}
return repeat;
}
Which has the followin output:
1 - 2
3 - 4 - 6
5
1 - 2
3 - 4 - 6
5
1 - 2
3 - 4 - 6
5
Inside your repeat method you can use following way to get repeated items
var repeated = str.GroupBy(s=>s)
.Where(grp=>grp.Count()>1)
.Select(y=>y.Key)
.ToList();

How to find the duplicates in the given string in c#

I want to find the duplicates for a given string, I tried for collections, It is working fine, but i don't know how to do it for a string.
Here is the code I tried for collections,
string name = "this is a a program program";
string[] arr = name.Split(' ');
var myList = new List<string>();
var duplicates = new List<string>();
foreach(string res in arr)
{
if (!myList.Contains(res))
{
myList.Add(res);
}
else
{
duplicates.Add(res);
}
}
foreach(string result in duplicates)
{
Console.WriteLine(result);
}
Console.ReadLine();
But I want to find the duplicates for the below string and to store it in an array. How to do that?
eg:- string aa = "elements";
In the above string i want to find the duplicate characters and store it in an array
Can anyone help me?
Linq solution:
string name = "this is a a program program";
String[] result = name.Split(' ')
.GroupBy(word => word)
.Where(chunk => chunk.Count() > 1)
.Select(chunk => chunk.Key)
.ToArray();
Console.Write(String.Join(Environment.NewLine, result));
The same princicple for duplicate characters within a string:
String source = "elements";
Char[] result = source
.GroupBy(c => c)
.Where(chunk => chunk.Count() > 1)
.Select(chunk => chunk.Key)
.ToArray();
// result = ['e']
Console.Write(String.Join(Environment.NewLine, result));
string name = "elements";
var myList = new List<char>();
var duplicates = new List<char>();
foreach (char res in name)
{
if (!myList.Contains(res))
{
myList.Add(res);
}
else if (!duplicates.Contains(res))
{
duplicates.Add(res);
}
}
foreach (char result in duplicates)
{
Console.WriteLine(result);
}
Console.ReadLine();
string is an array of chars. So, you can use your collection approach.
But, I would reccomend typed HashSet. Just load it with string and you'll get array of chars without duplicates, with preserved order.
take a look:
string s = "aaabbcdaaee";
HashSet<char> hash = new HashSet<char>(s);
HashSet<char> hashDup = new HashSet<char>();
foreach (var c in s)
if (hash.Contains(c))
hash.Remove(c);
else
hashDup.Add(c);
foreach (var x in hashDup)
Console.WriteLine(x);
Console.ReadKey();
Instead of a List<> i'd use a HashSet<> because it doesn't allow duplicates and Add returns false in that case. It's more efficient. I'd also use a Dictionary<TKey,Tvalue> instead of the list to track the count of each char:
string text = "elements";
var duplicates = new HashSet<char>();
var duplicateCounts = new Dictionary<char, int>();
foreach (char c in text)
{
int charCount = 0;
bool isDuplicate = duplicateCounts.TryGetValue(c, out charCount);
duplicateCounts[c] = ++charCount;
if (isDuplicate)
duplicates.Add(c);
}
Now you have all unique duplicate chars in the HashSet and the count of each unique char in the dictionary. In this example the set only contains e because it's three times in the string.
So you could output it in the following way:
foreach(char dup in duplicates)
Console.WriteLine("Duplicate char {0} appears {1} times in the text."
, dup
, duplicateCounts[dup]);
For what it's worth, here's a LINQ one-liner which also creates a Dictionary that only contains the duplicate chars and their count:
Dictionary<char, int> duplicateCounts = text
.GroupBy(c => c)
.Where(g => g.Count() > 1)
.ToDictionary(g => g.Key, g => g.Count());
I've shown it as second approach because you should first understand the standard way.
string name = "this is a a program program";
var arr = name.Split(' ').ToArray();
var dup = arr.Where(p => arr.Count(q => q == p) > 1).Select(p => p);
HashSet<string> hash = new HashSet<string>(dup);
string duplicate = string.Join(" ", hash);
You can do this through `LINQ
string name = "this is a a program program";
var d = name.Split(' ').GroupBy(x => x).Select(y => new { word = y.Key, Wordcount = y.Count() }).Where(z=>z.cou > 1).ToList();
Use LINQ to group values:
public static IEnumerable<T> GetDuplicates<T>(this IEnumerable<T> list)
{
return list.GroupBy(item => item).SelectMany(group => group.Skip(1));
}
public static bool HasDuplicates<T>(this IEnumerable<T> list)
{
return list.GetDuplicates().IsNotEmpty();
}
Then you use these extensions like this:
var list = new List<string> { "a", "b", "b", "c" };
var duplicatedValues = list.GetDuplicates();

Join two dictionaries by value diffs

I have these two dictionaries:
Dictionary<char, double> analyzed_symbols = new Dictionary<char, double>();
Dictionary<char, double> decode_symbols = new Dictionary<char, double>();
I need to create another dictionary that should have their keys as key and value, like this:
Dictionary<char, char> replace_symbols = new Dictionary<char, char>();
The condition to "join" them is that difference between values should be minimal, like this:
Math.Min(Math.Abs(analyzed_symbols[key] - decode_symbols[key]))
I guess I should use LINQ for this purpose but can't figure out how to write query properly.
Data Sample:
analyzed_symbols = [a, 173], [b, 1522], [z, 99]
decode_symbols = [в, 100], [д, 185], [e, 1622]
For these dicts output data should look like this:
replace_symbols = [z, в], [b, е], [a, д]
I've found question that is pretty close to what I need, but not exactly. Snowy asks there about one close value, but I need to do the same thing for two dictionaries.
This is my take on it:
var analyzed_symbols = new Dictionary<char, double>(){ {'a', 173}, {'b', 1522}, {'z', 99} };
var decode_symbols = new Dictionary<char, double>(){ {'в', 100}, {'д', 185}, {'e', 1622} };
var q = from a in analyzed_symbols
from d in decode_symbols
let tmp = new { A = a.Key, D = d.Key, Value = Math.Abs(a.Value - d.Value) }
group tmp by tmp.A into g
select new
{
Key = g.Key,
Value = g.OrderBy (x => x.Value).Select (x => x.D).First()
};
var replace_symbols = q.ToDictionary (x => x.Key, x => x.Value);
Okay, I'll try. I divided into several queries, because it's more readable that way.
//sorting values of the dictionaries to easily get closest
var analyzedSortedValues = analyzed_symbols.Values.OrderBy(k => k);
var decodeSortedValues = decode_symbols.Values.OrderBy(k => k);
//creating pairs of the closest values. Here I use iterator index i to skip
//some values that have been used already (is it correct?)
var t = analyzedSortedValues.Select((k, i) => new { a = k, d = decodeSortedValues.Skip(i).Any() ? decodeSortedValues.Skip(i).First() : -1 });
//printing results by getting appropriate keys from corresponding dictionaries
foreach (var item in t)
{
Console.WriteLine("[{0}, {1}]", analyzed_symbols.FirstOrDefault(kvp => kvp.Value == item.a).Key, decode_symbols.FirstOrDefault(kvp => kvp.Value == item.d).Key);
}
I am not exactly sure how to do it via LINQ but here is the longhand version of what you want to do.
private static Dictionary<char, char> BuildReplacementDictionary(Dictionary<char, double> analyzedSymbols,
Dictionary<char, double> decodeSymbols)
{
Dictionary<char, char> replaceSymbols = new Dictionary<char, char>(analyzedSymbols.Count);
foreach (KeyValuePair<char, double> analyzedKvp in analyzedSymbols)
{
double bestMatchValue = double.MaxValue;
foreach (KeyValuePair<char, double> decodeKvp in decodeSymbols)
{
var testValue = Math.Abs(analyzedKvp.Value - decodeKvp.Value);
if (testValue <= bestMatchValue)
{
bestMatchValue = testValue;
replaceSymbols[analyzedKvp.Key] = decodeKvp.Key;
}
}
}
return replaceSymbols;
}
What it does is it goes through each element of the analyzed dictionary, test every element of the decoded dictionary, and if that match is the same or better than the previous match it found it will use the new value from the decoded dictionary.

How to modify key in a dictionary in C#

How can I change the value of a number of keys in a dictionary.
I have the following dictionary :
SortedDictionary<int,SortedDictionary<string,List<string>>>
I want to loop through this sorted dictionary and change the key to key+1 if the key value is greater than a certain amount.
As Jason said, you can't change the key of an existing dictionary entry. You'll have to remove/add using a new key like so:
// we need to cache the keys to update since we can't
// modify the collection during enumeration
var keysToUpdate = new List<int>();
foreach (var entry in dict)
{
if (entry.Key < MinKeyValue)
{
keysToUpdate.Add(entry.Key);
}
}
foreach (int keyToUpdate in keysToUpdate)
{
SortedDictionary<string, List<string>> value = dict[keyToUpdate];
int newKey = keyToUpdate + 1;
// increment the key until arriving at one that doesn't already exist
while (dict.ContainsKey(newKey))
{
newKey++;
}
dict.Remove(keyToUpdate);
dict.Add(newKey, value);
}
You need to remove the items and re-add them with their new key. Per MSDN:
Keys must be immutable as long as they are used as keys in the SortedDictionary(TKey, TValue).
You can use LINQ statment for it
var maxValue = 10
sd= sd.ToDictionary(d => d.key > maxValue ? d.key : d.Key +1, d=> d.Value);
If you don't mind recreating the dictionary, you could use a LINQ statment.
var dictionary = new SortedDictionary<int, SortedDictionary<string, List<string>>>();
var insertAt = 10;
var newValues = dictionary.ToDictionary(
x => x.Key < insertAt ? x.Key : x.Key + 1,
x => x.Value);
return new SortedDictionary<int, SortedDictionary<string, List<string>>>(newValues);
or
var dictionary = new SortedDictionary<int, SortedDictionary<string, List<string>>>();
var insertAt = 10;
var newValues = dictionary.ToDictionary(
x => x.Key < insertAt ? x.Key : x.Key + 1,
x => x.Value);
dictionary.Clear();
foreach(var item in newValues) dictionary.Add(item.Key, item.Value);

Categories