Array of concatenated strings into Dictionary<int, int> - c#

I have an array of strings. Each string is two numbers separated with a "|".
How can I get this array of string into Dictionary<int,int> without looping through the array, splitting each string and adding to the dictionary.
Is there a better way?

simply,
var result = strings
.Select(s => s.Split('|'))
.ToDictionary(a => int.Parse(a[0]), a => int.Parse(a[1]));
if duplicates are allowed,
var result = strings
.Select(s => s.Split('|'))
.ToLookup(a => int.Parse(a[0]), a => int.Parse(a[1]));

You can use ToDictionary method:
var dictionary = stringArray.ToDictionary(x => x.Split('|')[0], x => x.Split('|')[1]);
But you should be aware that this will throw an exception if there are duplicate keys.

Related

.ToString().ToList() returns a New List<char>

So I have a Query that searches for various items. But I just want their Id so I used a projection to return me only the Ids and not the other elements of the item. but converting from ObjectId .ToString() and then .ToList() returns me a List<char> instead List<string>
var items = await this.ItemAppService.GetAllAsync(expression,
x => new
{
Ids = x.Id.ToString().ToList(),
});
var Ids = items.SelectMany(x => x.Ids.Select(x => x)).ToList();
I would like to understand why i'm returning a List<Char> and how I convert it to List<String>
The first ToList is unnecessary, you want strings, and with that ToList() invocation you are converting your strings to char arrays. So the code should be rewritten as:
var items = await this.ItemAppService.GetAllAsync(expression,
x => new
{
Id = x.Id.ToString(),
});
var ids = items.Select(x => x.Id).ToList();
.ToString().ToList() returns a New List<char>
Yes, because a string is an IEnumerable<char> (a bunch of characters that can be enumerated over). ToList is an extension method on all IEnumerable<T>, that returns a List<T>.
So the Ids property in the anonymous object is already a List<char>:
x => new
{
// this "Ids" is already a List<char>
Ids = x.Id.ToString().ToList(),
});
You then do some more shuffling on it, but not meaningfully changing anything. x.Ids.Select(x => x) returns an Innumerable<char> with the same contents as x.Ids. And SelectMany adds up all those IEnumerable<char> in each of the anonymous objects into one big IEnumerable<char>, which you then convert to a list.
I'm not sure why you have used an anonymous object here. If you just want a List<string> with all the IDs, just do:
var ids = await this.ItemAppService.GetAllAsync(
expression,
// assuming x.Id is not already a string
x => x.Id.ToString()
).ToList();

How to rank elements in c# especially when it has duplicates

I have a requirement to rank the array elements and the array has duplicate values. I tried following this Ranking items in a list with LINQ but this doesn't work when the array has duplicate values in it. Any easy way to do it in c#?
For Example :
input = [650,150,150,200]
output = [1,3,3,2]
For Example :
input = [650,200,200,150]
output = [1,2,2,3]
Update: The requirement is as below, what if I add one more element to the array
Ex: [650,150,150,200,100] output needs to be [1,3,3,2,5] instead of [1,3,3,2,4]
You can create a dictionary as rank-lookup source:
int[] array = new[] {650,150,150,200};
Dictionary<int, int> numRanks = array
.GroupBy(i => i)
.OrderByDescending(g => g.Key)
.Select((g, index) => (num:g.Key, rank:index+1))
.ToDictionary(x => x.num, x => x.rank);
int[] result = array.Select(i => numRanks[i]).ToArray();
For your updated requirement you could use a similar approach using a Lookup<TKey, TValue>:
var rankLookup = array
.OrderByDescending(i => i)
.Select((num, index) => (num, index))
.ToLookup(x => x.num, x => x.index + 1);
int[] result = array.Select(i => rankLookup[i].First()).ToArray();
The lookup is like a dictionary that allows duplicate keys. You need to use First here because you are just interested in the rank. If you'd use Count() you'd know how many duplicates it had.
You could create an array of items, distinct and in order, then use the indices to determine the rank of each item.
var ranks = input.Distinct().OrderByDescending(x => x).ToArray();
var ranked = input.Select(x => Array.IndexOf(ranks, x) + 1);
Working example
Update after comment
If rankings need to be skipped, just remove the Distinct:
var ranks = input.OrderByDescending(x => x).ToArray();
var ranked = input.Select(x => Array.IndexOf(ranks, x) + 1);
Array.IndexOf will take the first element when there are duplicates.
Working example

Why isn't this LINQ parsing a file properly?

I have a file with a simple key,value format, one per line.
e.g:
word1,filepath1
word2,filepath2
word3,filepath5
I'm trying to read this into a Dictionary<string,string> in one go with LINQ. There are some duplicates in the file (where the first part - the first string - is the duplicate). In this case, I'm ok with dropping the duplicates.
This is my LINQ which isn't working:
var indexes = File.ReadAllLines(indexFileName)
.Select(x => x.Split(','))
.GroupBy(x=>x[0])
.ToDictionary(x => x.Key, x => x.ElementAt(1));
The ToDictionary part is confusing me, how do I retrieve the first value from the group and assign it to the value of the dictionary?
I get a System.ArgumentOutOfRangeException: 'Specified argument was out of the range of valid values.' exception.
var indexes = File.ReadAllLines(indexFileName)
.Select(x => x.Split(','))
.GroupBy(x => x[0])
.ToDictionary(x => x.Key, x => x.First()[1]);
So the problem here is that you're grouping arrays, not strings. Therefore the group objects you're dealing with in the ToDictionary() lambda are enumerations of arrays, not of strings. g.ElementAt(0) isn't a string. It's the first array of strings:
When
g.Key == "word1"
then g.ElementAt(0) is...
{ "word1", "filepath1" }
So you want g.ElementAt(0).ElementAt(1), or g.First()[0], or something to that effect.
That seems painfully obvious in hindsight, but unfortunately only in hindsight, for me.
I would suggest that after you accept Matthew Whited's answer, you clarify the code by turning the split lines into anonymous objects as soon as you can. ElementAt(1) doesn't communicate much.
var indexes =
File.ReadAllLines(indexFileName)
.Where(s => !String.IsNullOrEmpty(s))
.Select(x => x.Split(','))
// Turn the array into something self-documenting
.Select(a => new { Word = a[0], Path = a[1] })
.GroupBy(o => o.Word)
.ToDictionary(g => g.Key, g => g.First().Path)
;
Converting each line to an object makes it easier for me to think about, and Intellisense starts playing on your team as well.

Convert String list to a dictionary

I Have a string list like this ["saman=1", "kaman=2"]
How may I convert this to a dictionary like {Saman:1 , kaman:2}
strList.Select(k,v =>new {k,v} , k=> k.split('=')[0], val => v.split('=')[1]);
This should work:
strList.ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1])
If you want Dictionary<string, int> you can parse the Value to integer:
strList.ToDictionary(x => x.Split('=')[0], x => int.Parse(x.Split('=')[1]))
You should split by ", " first, and then split each item by = to get key/value pairs.
Additional Trim call will get rid of [" at the beginning and "] at the end of your input string.
var input = #"[""saman=1"", ""kaman=2""]";
var dict = input.Trim('[', '"', ']')
.Split(new [] {#""", """}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split('='))
.ToDictionary(x => x[0], x => x[1]);
Very, very simply with LINQ:
IDictionary<string, string> dictionary =
list.ToDictionary(pair => pair.Key, pair => pair.Value);
Note that this will fail if there are any duplicate keys - I assume that's okay?

How can I order a Dictionary<string,string> by a substring within the value?

I have a Dictionary <string, string> where the value is a concatenation of substrings delimited with a :. For example, 123:456:Bob:Smith.
I would like to order the dictionary by the last substring (Smith) ascending, and preferably like this:
orderedDictionary = unordered
.OrderBy(x => x.Value)
.ToDictionary(x => x.Key, x => x.Value);
So, I need to somehow treat the x.Value as a string and sort by extracting the fourth substring. Any ideas?
var ordered = unordered.OrderBy(x => x.Value.Split(':').Last())
.ToDictionary(x => x.Key, x => x.Value);
Try
orderedDictionary = unordered.OrderBy(x => x.Value.Substring(x.Value.LastIndexOf(":"))).ToDictionary(x => x.Key, x => x.Value);
Take a look at the OrderBy Method of IDictionary, specifically this one http://msdn.microsoft.com/en-us/library/bb549422.aspx noting the comparerparameter. That should point you in the right direction and I think you'll find learning the remainder of benefit.

Categories

Resources