// 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
Related
I am working on project which is asp.net mvc core. I want to replace string list of duplicate values to one with comma separated,
List<string> stringList = surveylist.Split('&').ToList();
I have string list
This generate following output:
7=55
6=33
5=MCC
4=GHI
3=ABC
1003=DEF
1003=ABC
1=JKL
And I want to change output like this
7=55
6=33
5=MCC
4=GHI
3=ABC
1003=DEF,ABC
1=JKL
Duplicate items values should be comma separated.
There are probably 20 ways to do this. One simple one would be:
List<string> newStringList = stringList
.Select(a => new { KeyValue = a.Split("=") })
.GroupBy(a => a.KeyValue[0])
.Select(a => $"{a.Select(x => x.KeyValue[0]).First()}={string.Join(",", a.Select(x => x.KeyValue[1]))}")
.ToList();
Take a look at your output. Notice that an equal sign separates each string into a key-value pair. Think about how you want to approach this problem. Is a list of strings really the structure you want to build on? You could take a different approach and use a list of KeyValuePairs or a Dictionary instead.
If you really need to do it with a List, then look at the methods LINQ's Enumerable has to offer. Namely Select and GroupBy.
You can use Select to split once more on the equal sign: .Select(s => s.Split('=')).
You can use GroupBy to group values by a key: .GroupBy(pair => pair[0]).
To join it back to a string, you can use a Select again.
An end result could look something like this:
List<string> stringList = values.Split('&')
.Select(s => {
string[] pair = s.Split('=');
return new { Key = pair[0], Value = pair[1] };
})
.GroupBy(pair => pair.Key)
.Select(g => string.Concat(
g.Key,
'=',
string.Join(
", ",
g.Select(pair => pair.Value)
)
))
.ToList();
The group contains pairs so you need to select the value of each pair and join them into a string.
I am having a list of string which contains some value and I want to compare values of 2 positions from list and remove matching items from list.
Code :
var list = new List<string>();
list.Add("Employee1");
list.Add("Account");
list.Add("100.5600,A+ ,John");
list.Add("1.00000,A+ ,John");
list.Add("USA");
Now i want to compare 2nd and 3rd position :
list.Add("100.5600,A+ ,John");
list.Add("1.00000,A+ ,John");
Compare above 2 records and remove matching records like below:
Expected output :
list.Add("100.5600");
list.Add("1.00000");
This is how i am trying to do :
var source = list[2].Split(',').Select(p => p.Trim());
var target = list[3].Split(',').Select(p => p.Trim());
var result = source.Except(target);
But the problem is I am only getting 100.5600 as output.
Is it possible to compare and update non matching records in existing list?
How about this "beauty"
var list = new List<string>();
list.Add("Employee1");
list.Add("Account");
list.Add("100.5600,A+ ,John");
list.Add("1.00000,A+ ,John");
list.Add("USA");
//prepare the list, I decided to make a tuple with the original string in the list and the splitted array
var preparedItems = list.Select(x => (x, x.Split(',')));
//group the prepared list to get matching items for the 2nd and 3rd part of the split, I therefor used .Skip(1) on the previously prepared array
var groupedItems = preparedItems.GroupBy(x => string.Join(",", x.Item2.Skip(1).Select(y => y.Trim())));
//"evaluate" the group by saying if the items in the group is > 1 only use the first part of the prepared array and if it doesnt have more than one entry use the orignal string
var evaluatedItems = groupedItems.SelectMany(x => x.Count() > 1 ? x.Select(y => y.Item2[0]) : x.Select(y => y.Item1));
//replace the orignal list with the new result
list = evaluatedItems.ToList();
Edit - preserve original order:
//extended the prepare routine with a third part the index to Keep track of the ordering of the original list
//so the tuple now consits of 3 parts instead of 2 - ([item], [index], [splittedArray])
var preparedItems = list.Select((x, i) => (x, i, x.Split(',')));
//changed to use Item3 intead of Item2 - since the Array now is on third position
var groupedItems = preparedItems.GroupBy(x => string.Join(",", x.Item3.Skip(1).Select(y => y.Trim())));
//instead of returning the simple string here already, return a tuple with the index (y.Item2) and the correct string
var evaluatedItems = groupedItems.SelectMany(x => x.Count() > 1 ? x.Select(y => (y.Item2, y.Item3[0])) : x.Select(y => (y.Item2, y.Item1)));
//now order by the new tuple x.Item1 and only return x.Item2
var orderedItems = evaluatedItems.OrderBy(x => x.Item1).Select(x => x.Item2);
list = orderedItems.ToList();
//one-liner - isn't that a beauty
list = list.Select((x, i) => (x, i, x.Split(','))).GroupBy(x => string.Join(",", x.Item3.Skip(1).Select(y => y.Trim()))).SelectMany(x => x.Count() > 1 ? x.Select(y => (y.Item2, y.Item3[0])) : x.Select(y => (y.Item2, y.Item1))).OrderBy(x => x.Item1).Select(x => x.Item2).ToList();
You may get it easily by checking if items in one is not contained in the other:
var result = source.Where(x => !target.Contains(x));
To update your old list:
var source = string.Join(",", source.Where(x => !target.Contains(x)));
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.
I've checked many solutions on different sites but couldn't find what I was looking for. I'm working on a dictionary object with different Values against Keys. The structure is as follows:
Key Value
6 4
3 4
2 2
1 1
If they dictionary contains elements like this, the output should be 6 and 3, if Key (6) has the highest value, it should print only 6. However, if all the values are same against each key, it should print all the keys.
Trying to use the following but it only prints the highest Value.
var Keys_ = dicCommon.GroupBy(x => x.Value).Max(p => p.Key);
Any ideas
Instead of using Max(x=>x.Key) use .OrderByDescending(x=>x.Key) and .FirstOrDefault() that will give you the group that has the max value. You then can itterate over the group and display whatever you need.
var dicCommon = new Dictionary<int, int>();
dicCommon.Add(6, 4);
dicCommon.Add(3, 4);
dicCommon.Add(2, 2);
dicCommon.Add(1, 1);
var maxGroup = dicCommon.GroupBy(x => x.Value).OrderByDescending(x => x.Key).FirstOrDefault();
foreach (var keyValuePair in maxGroup)
{
Console.WriteLine("Key: {0}, Value {1}", keyValuePair.Key, keyValuePair.Value);
}
Run Code
First off a query can't return one and more than one result at the same time.So you need to pick one.
In this case if you want all Keys that has the highest corresponding Value, you can sort the groups based on Value then just get the first group which has the highest Value:
var Keys_ = dicCommon.GroupBy(x => x.Value)
.OrderByDescending(g => g.Key)
.First()
.Select(x => x.Key)
.ToList();
var keys = String.Join(",", dicCommon
.OrderByDescending(x=>x.Value)
.GroupBy(x => x.Value)
.First()
.Select(x=>x.Key));
You’re almost there:
dicCommon.GroupBy(x => x.Value)
.OrderByDescending(pair => pair.First().Value)
.First().Select(pair => pair.Key).ToList()
GroupBy returns an enumerable of IGrouping. So sort these descending by value, then get the first, and select the key of each containing element.
Since this requires sorting, the runtime complexity is not linear, although we can easily do that. One way would be figuring out the maximum value first and then getting all the keys where the value is equal to that:
int maxValue = dicCommon.Max(x => x.Value);
List<int> maxKeys = dicCommon.Where(x => x.Value == maxValue).Select(x => x.Key).ToList();
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);