Assignment Error in LINQ - c#

I am changing string array into dictionary collection.
string str = "When everybody is somebody then no one is anybody";
char[] separation = { ' ', '.', ',' };
var splitted=str.Split(separation);
(Absolutely it is not a good design,but i wish to know the logic)
When i build query
var indexed = (from i in Enumerable.Range(1, splitted.Length)
from strn in splitted
select new { i, strn }).ToDictionary(x => x.i, x => x.strn);
I received "Key already found in dictionary" . I am supplying unique keys as enumerated
values.

No, you're not supplying unique keys. You're supplying:
1 When
2 When
3 When
...
9 When
1 everybody
2 everybody
etc
... so you'll be giving "1" as a key twice (then you'd supply "2" as a key again if you ever got that far).
What result are you actually trying to achieve? If it's: 1 -> When, 2 -> everybody etc then you want:
var indexed = splitted.Select((value, index) => new { value, index })
.ToDictionary(x => x.index + 1, x => x.value);

Related

Remove From Duplicate Starting Names From List Linq

I have a list of paths that look like
//servername/d$/directory
I am getting the serverName from the path with the following
var host = somePath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
I want to refine this list to only 1 server Name listed (say the first one found)
Example
if the list contains
//serverA/d$/directoryA
//serverA/d$/directoryB
//serverA/d$/directoryC
//serverB/d$/directoryD
//serverB/d$/directoryE
the list would turn into
//serverA/d$/directoryA
//serverB/d$/directoryD
You can group them by the server name (by trimming the start and splitting on the / character and taking the first item), and then select the first item from each group into a new list:
var serverNames = new List<string>
{
"//serverA/d$/directoryA",
"//serverA/d$/directoryB",
"//serverA/d$/directoryC",
"//serverB/d$/directoryD",
"//serverB/d$/directoryE",
};
var results = serverNames
.GroupBy(name => name.TrimStart('/').Split('/')[0])
.Select(group => group.First())
.ToList();
From your first code example it's not clear if the paths begin with \, so to handle both cases you can do:
var results = serverNames
.GroupBy(name => name.TrimStart('\\', '/', ' ').Split('\\', '/')[0])
.Select(group => group.First())
.ToList();

C# LINQ Query / Projection from FormCollection

I have a dynamic form which I cannot use MVC's binding with. When I post this FormCollection to my controller, simplified, I have the following data in the form collection:
public ActionResult Foo(FormCollection coll)
...
coll["Data0Key"] contains "category"
coll["Data0Value"] contains "123"
coll["Data1Key"] contains "location"
coll["Data1Value"] contains "21"
coll["Data7Key"] contains "area"
coll["Data7Value"] contains "test"
coll["SomethingElse"] contains "irrelevent"
.. I have an unknown number of these and would like to create key-value pairs from the seperate key and value objects in the collection
I have been attempting along the lines of;
var settings = coll.AllKeys
.Where(k => k.StartsWith("Data"))
.ToDictionary(k => k, k => coll[k]);
which gives me a dictionary of:
Data0Key, category
Data0Value, 123
Data1Key, location
Data1Value, 21
Data7Key, area
Data7Value, test
What I would really like to have is a collection of key value pairs structured like;
category, 123
location, 21
area, test
Is what I am trying to achieve possible at all, or do I need to find a different approach?
I think you want to only iterate over the "DataxKey" parts, then look up the values. Something like:
.Where(k => k.StartsWith("Data") && k.EndsWith("Key"))
.ToDictionary(k => coll[k], k => coll[k.Replace("Key", "Value")]);
This assumes that every "Data0Key" also has a "Data0Value" matching pair, otherwise it's going to dereference a key that doesn't exist from coll.
This can be done with a relatively straightforward LINQ query:
var data = new Dictionary<string,object> {
["Data0Key"] = "category"
, ["Data0Value"] = "123"
, ["Data1Key"] = "location"
, ["Data1Value"] = "21"
, ["Data7Key"] = "area"
, ["Data7Value"] = "test"
, ["SomethingElse"] = "irrelevent"
};
var kvp = data
.Where(p => p.Key.StartsWith("Data") && (p.Key.EndsWith("Key") || p.Key.EndsWith("Value")))
.Select(p => new {
Key = p.Key.Substring(0, p.Key.Length - (p.Key.EndsWith("Key") ? 3 : 5))
, IsKey = p.Key.EndsWith("Key")
, p.Value
})
.GroupBy(p => p.Key)
.Where(g => g.Count(p => p.IsKey) == 1 && g.Count(p => !p.IsKey) == 1)
.ToDictionary(g => (string)g.Single(p => p.IsKey).Value, g => g.Single(p => !p.IsKey).Value);
foreach (var p in kvp) {
Console.WriteLine("{0} - {1}", p.Key, p.Value);
}
Here is a line-by-line explanation of what is done:
First, irrelevant items are filtered out by ensuring that only "Data" prefixes are kept, and that the suffixes are "Key" or "Value"
Next, group key is extracted by removing "Key" or "Value" suffix; the Value is added to the list, along with IsKey flag indicating if an item was a key or a value
Items are grouped by the group key ("Data0", "Data7", etc.)
Each group is checked to contain exactly one key and one value; incomplete groups are discarded
Finally, groups are converted to a dictionary of key-value pairs.

Dictionary <string, uint> to comma-separated string?

// Below is an array which contains master data
string[] masterData = { "324", "233", "32", "423", "23435" };
// Below string will always contain a comma separated subset of above master data
string abc = "233,423,99";
What I need is to have a dictionary having string key of all masterData array values & have the value set to 1 only for those keys which are in the string abc. All other keys need value of 0. In this case, dictionary should be of type string & value should be of type uint. In this case, dictionary should be as shown below:
String uint
"324" 0
"233" 1
"32" 0
"423" 1
"23435" 0
Note that 99 from string is ignored as only all master data should be present in dictionary.
Is there a way to do it using linq? If not, what other shorted way is it possible with?
Also, how do I perform the above operation in the reverse way using linq or any other shorted approach i.e. convert dictionary<string, uint> back to comma separated string? And this string should contain only those keys from the dictionary whose value is 1.
Note: dictionary value can be either 1 or 0 only.
Any help is much appreciated.
var abcArray = abc.Split(',');
masterData.ToDictionary(p => p, p => (uint)(abcArray.Contains(p) ? 1 : 0));
You can use Concat to add together the first array and the split string, then use GroupBy to group the same numbers, then just create the Dictionary from that, the duplicated items will have a Count greater than 1.
var dict = masterData
.Concat(abc.Split(','))
.GroupBy(x => x)
.ToDictionary(k => k.Key, v => v.Count() > 1 ? 1 : 0);
To reverse, select all the strings where the Value is 1 and Join the sting
string result = string.Join(",", dict.Where(x => x.Value > 0)
.Select(k => k.Key)
.ToArray());
string abc = "233,423,99";
var subset = abc.Split(',');
var result = masterData.ToDictionary(k => k, k => Convert.ToUInt32(subset.Contains(k)));
var reversedResult = result.Where(p => p.Value == 1).Select(p => p.Key).ToArray();
https://dotnetfiddle.net/5eU5B0

IEnumerable<string> to Dictionary<char, IEnumerable<string>>

I suppose that this question might partially duplicate other similar questions, but i'm having troubles with such a situation:
I want to extract from some string sentences
For example from
`string sentence = "We can store these chars in separate variables. We can also test against other string characters.";`
I want to build an IEnumerable words;
var separators = new[] {',', ' ', '.'};
IEnumerable<string> words = sentence.Split(separators, StringSplitOptions.RemoveEmptyEntries);
After that, go throught all these words and take firs character into a distinct ascending ordered collection of characters.
var firstChars = words.Select(x => x.ToCharArray().First()).OrderBy(x => x).Distinct();
After that, go through both collections and for each character in firstChars get all items from words which has the first character equal with current character and create a Dictionary<char, IEnumerable<string>> dictionary.
I'm doing this way:
var dictionary = (from k in firstChars
from v in words
where v.ToCharArray().First().Equals(k)
select new { k, v })
.ToDictionary(x => x);
and here is the problem: An item with the same key has already been added.
Whis is because into that dictionary It is going to add an existing character.
I included a GroupBy extension into my query
var dictionary = (from k in firstChars
from v in words
where v.ToCharArray().First().Equals(k)
select new { k, v })
.GroupBy(x => x)
.ToDictionary(x => x);
The solution above gives makes all OK, but it gives me other type than I need.
What I should do to get as result an Dictionary<char, IEnumerable<string>>dictionary but not Dictionary<IGouping<'a,'a>> ?
The result which I want is as in the bellow image:
But here I have to iterate with 2 foreach(s) which will Show me wat i want... I cannot understand well how this happens ...
Any suggestion and advice will be welcome. Thank you.
As the relation is one to many, you can use a lookup instead of a dictionary:
var lookup = words.ToLookup(word => word[0]);
loopkup['s'] -> store, separate... as an IEnumerable<string>
And if you want to display the key/values sorted by first char:
for (var sortedEntry in lookup.OrderBy(entry => entry.Key))
{
Console.WriteLine(string.Format("First letter: {0}", sortedEntry.Key);
foreach (string word in sortedEntry)
{
Console.WriteLine(word);
}
}
You can do this:
var words = ...
var dictionary = words.GroupBy(w => w[0])
.ToDictionary(g => g.Key, g => g.AsEnumerable());
But for matter, why not use an ILookup?
var lookup = words.ToLookup(w => w[0]);

Converting Collection of Strings to Dictionary

This is probably a simple question, but the answer is eluding me.
I have a collection of strings that I'm trying to convert to a dictionary.
Each string in the collection is a comma-separated list of values that I obtained from a regex match. I would like the key for each entry in the dictionary to be the fourth element in the comma-separated list, and the corresponding value to be the second element in the comma-separated list.
When I attempt a direct call to ToDictionary, I end up in some kind of loop that appears to kick me of the BackgroundWorker thread I'm in:
var MoveFromItems = matches.Cast<Match>()
.SelectMany(m => m.Groups["args"].Captures
.Cast<Capture>().Select(c => c.Value));
var dictionary1 = MoveFromItems.ToDictionary(s => s.Split(',')[3],
s => s.Split(',')[1]);
When I create the dictionary manually, everything works fine:
var MoveFroms = new Dictionary<string, string>();
foreach(string sItem in MoveFromItems)
{
string sKey = sItem.Split(',')[3];
string sVal = sItem.Split(',')[1];
if(!MoveFroms.ContainsKey(sKey))
MoveFroms[sKey.ToUpper()] = sVal;
}
I appreciate any help you might be able to provide.
The problem is most likely that the keys have duplicates. You have three options.
Keep First Entry (This is what you're currently doing in the foreach loop)
Keys only have one entry, the first one that shows up - meaning you can have a Dictionary:
var first = MoveFromItems.Select(x => x.Split(','))
.GroupBy(x => x[3])
.ToDictionary(x => x.Key, x => x.First()[1]);
Keep All Entries, Grouped
Keys will have more than one entry (each key returns an Enumerable), and you use a Lookup instead of a Dictionary:
var lookup = MoveFromItems.Select(x => x.Split(','))
.ToLookup(x => x[3], x => x[1]);
Keep All Entries, Flattened
No such thing as a key, simply a flattened list of entries:
var flat = MoveFromItems.Select(x => x.Split(','))
.Select(x => new KeyValuePair<string,string>(x[3], x[1]));
You could also use a tuple here (Tuple.Create(x[3], x[1]);) instead.
Note: You will need to decide where/if you want the keys to be upper or lower case in these cases. I haven't done anything related to that yet. If you want to store the key as upper, just change x[3] to x[3].ToUpper() in everything above.
This splits each item and selects key out of the 4th split-value, and value out of the 2nd split-value, all into a dictionary.
var dictionary = MoveFromItems.Select(s => s.Split(','))
.ToDictionary(split => split[3],
split => split[1]);
There is no point in splitting the string twice, just to use different indices.
This would be just like saving the split results into a local variable, then using it to access index 3 and 1.
However, if indeed you don't know if keys might reoccur, I would go for the simple loop you've implemented, without a doubt.
Although you have a small bug in your loop:
MoveFroms = new Dictionary<string, string>();
foreach(string sItem in MoveFromItems)
{
string sKey = sItem.Split(',')[3];
string sVal = sItem.Split(',')[1];
// sKey might not exist as a key
if (!MoveFroms.ContainsKey(sKey))
//if (!MoveFroms.ContainsKey(sKey.ToUpper()))
{
// but sKey.ToUpper() might exist!
MoveFroms[sKey.ToUpper()] = sVal;
}
}
Should do ContainsKey(sKey.ToUpper()) in your condition as well, if you really want the key all upper cases.
This will Split each string in MoveFromItems with ',' and from them make 4th item (3rd Index) as Key and 2nd item(1st Index) as Value.
var dict = MoveFromItems.Select(x => x.Split(','))
.ToLookup(x => x[3], x => x[1]);

Categories