Convert List of Dictionary<string, List> into list C# Linq - c#

This is my source type => IEnumerable<IDictionary<string, IEnumerable<string>>>
This is my target type => IEnumerable<string>
Expected Output
List of strings
[
"ca:aws_client:firstElementIntheList]",
"ca:aws_client:secondElementInTheList]"
]
Actual Output
List of strings
[
"ca:aws_client:System.Linq.Enumerable+SelectListIterator`2[System.String,System.String]",
"ca:aws_client:System.Linq.Enumerable+SelectListIterator`2[System.String,System.String]"
]
Code
input
.ToList()
.SelectMany(d => d)
.Select(i => $"ca:{i.Key}:{i.Value.Select(l => l)}")
.ToList()

You're looking to use a result selector in SelectMany instead of a second select statement.
Something like this may be what you want:
var dict = new Dictionary<string, IEnumerable<string>>
{
{"one", new List<string> {"1","2","3"}},
{"two", new List<string> {"1","2","3"}}
};
var res = dict.SelectMany(d => d.Value, (a, b) => $"ca:{a.Key}:{b}");
foreach(var val in res)
Console.WriteLine(val);
/* output:
ca:one:1
ca:one:2
ca:one:3
ca:two:1
ca:two:2
ca:two:3
*/
Edit:
I've noticed you're actually using a List of Dictionaries. The solution is largely the same, but with more SelectMany.
var list = new List<Dictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>> {
{"one", new List<string> {"1","2","3"}},
{"two", new List<string> {"1","2","3"}}
},
new Dictionary<string, IEnumerable<string>> {
{"three", new List<string> {"1","2","3"}},
{"four", new List<string> {"1","2","3"}}
}
};
var res = list.SelectMany(x => x)
.SelectMany(d => d.Value, (a, b) => $"ca:{a.Key}:{b}");
foreach(var val in res)
Console.WriteLine(val);
If someone can suggest a clean way to handle the multiple method syntax SelectMany other than stacking them, I'd like to know for my own edification.

Related

Comparing two dictionaries (Key, value) and return Keys that doesn't have same value

I'm bit new to c# and want to identify keys that doesn't have same value while comparing two dictionaries.
The dictionary I have is of dict => KeyValuePair<string, string>. And I have two dictionaries like -
dict1 => {"a":"False","b":"amazonaws.com","c":"True"}
dict2 => {"a":"True","b":"amazonaws.com","c":"False"}
I want to compare both dictionaries and return a variable which will have Keys ["a", "c"] as for the above example, those keys have different value.
Currently the logic I have written will only differentiate keys that's not there in the other dictionary.
Dictionary dictExcept = null;
foreach (IDictionary kvp in dict1.Cast<object>().Where(kvp => !dict2.Contains(kvp)))
{
dictExcept.Add(kvp.Keys, kvp.Values);
}
return dictExcept ;
You can try using TryGetValue:
using System.Linq;
...
var dictExcept = dict1
.Where(pair => dict2.TryGetValue(pair.Key, out var value) &&
pair.Value != value)
.ToDictionary(pair => pair.Key,
pair => (first: pair.Value, second: dict2[pair.Key]));
Here we for each key value pair from dict1 try to get corresponding value from dict2:
// dict2 has pair.Key it corresponds to value...
dict2.TryGetValue(pair.Key, out var value) &&
// however value from dict2 != value from dict1
pair.Value != value
The very same idea if you prefer to use foreach (no Linq solution):
var dictExcept = new Dictionary<string, (string first, string second)>();
foreach (var pair in dict1)
if (dict2.TryGetValue(pair.Key, out var value) && value != pair.Value)
dictExcept.Add(pair.Key, (pair.Value, value));
Demo: (fiddle)
var dict1 = new Dictionary<string, string> {
{ "a", "False" }, { "b", "False" }, { "c", "True" }, { "d", "dict1 only" } };
var dict2 = new Dictionary<string, string> {
{ "a", "False" }, { "b", "True" }, { "c", "False" }, { "e", "dict2 only" } };
var dictExcept = dict1
.Where(pair => dict2.TryGetValue(pair.Key, out var value) &&
pair.Value != value)
.ToDictionary(pair => pair.Key,
pair => (first: pair.Value, second: dict2[pair.Key]));
string report = string.Join(Environment.NewLine, dictExcept
.Select(pair => $"Key: {pair.Key}; Values: {pair.Value}"));
Console.Write(report);
Outcome:
Key: b; Values: (False, True)
Key: c; Values: (True, False)
Providing the simplest answer because of your comment:
Both the dictionaries will have same keys, just that we need to identify keys that has different values
Working under that assumption that you don't need to account for missing keys, you can simply iterate over all the keys of one of the dictionaries, and compare the values found under that key.
var keysWithDifferentValues = new List<string>();
foreach (var kvp in dict1)
{
if(!kvp.Value.Equals(dict2[kvp.Key]))
keysWithDifferentValues.Add(kvp.Key);
}
This can be simplified using LINQ:
var keysWithDifferentValues =
dict1
.Where(kvp => !kvp.Value.Equals(dict2[kvp.Key]))
.Select(kvp => kvp.Key)
.ToList();
Since you have a dictionary named dictExcept, what about using Expect to do the job for you?
Produces the set difference of two sequences.
source
And in your case:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
static void Main(string[] args)
{
var a = new Dictionary<string, string>{{"a", "False"}, {"b", "False"}, {"c", "True"}};
var b = new Dictionary<string, string>{{"a", "False"}, {"b", "True"}, {"c", "False"}};
var c = a.Except(b).Select(x => x.Key);
c.Dump();
}
}
output
[ b, c ]
Try it online!
More examples with different cases:
static void Main(string[] args)
{
var a = new Dictionary<string, string>{{"a", "False"}, {"b", "False"}, {"c", "True"}};
var b = new Dictionary<string, string>{{"a", "False"}, {"b", "True"}, {"c", "False"}};
var c = a.Except(b).Select(x => x.Key);
// c is [ b ,c ]
a.Add("d", "foo");
var d = a.Except(b).Select(x => x.Key);
// d is [ b, c, d ]
b.Add("e", "foo");
var e = a.Except(b).Select(x => x.Key);
// e is still [ b, c, d ]
var e2 = (a.Except(b)).Union(b.Except(a)).Select(x => x.Key).Distinct();
// e is [ b, c, d, e ]
}
Try it Online!

Linq Query Dictionary where value not in List

I have a Dictionary and another List. What I am trying to achieve is a LINQ query to get all items out of the dictionary where any values from said dictionary are not in the List<string>.
I found this post to be helpful, Linq Query Dictionary where value in List. And was able to write the following LINQ expression.
What I have so far:
Data is the dictionary and PersonList is the list of strings.
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
However, my results never actually return anything.
Update:
Dictionary<string, string> Data = new Dictionary<string, string>
{
{ "John", "aaa" },
{ "Tom", "bbb" },
{ "David", "ccc" }
};
List<string> PersonList = new List<string>
{
"Tom",
"Peter"
};
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Is it possible that you made a typo here and meant to write this instead?
// changed to kvp.Value from kvp.Key
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
This searches the values from the dictionary in the list and retains those entries which are not in the list, as per your question.
Answering this myself as I've been looking into the problem from wrong angle.
Following works as expected:
Dictionary<string, string> Data = new Dictionary<string, string>
{
{ "John", "aaa" },
{ "Tom", "bbb" },
{ "David", "ccc" }
};
List<string> PersonList = new List<string>
{
"Tom",
"Peter"
};
List<string> PersonListNotInDictionary = PersonList.Where(pl => !Data.ContainsKey(pl))
.ToList();
It is possible that the contains doesn't work, because the items in the PersonList are other objects than the keys in your dictionary. The objects might be the same (have the same content), but if it are different object (different refences), than the contains will return false.
An example:
myObject = new myObject() { Id = 1 };
List<myObject> listOfObjects = new List<myObject>();
listOfObjects.Add(new myObject() { Id = 1 });
var result = listOfObjects.Contains(myObject); // returns false, because the item in the list is a different object than myObject
result = listOfObjects.Any(obj => obj.Id == myObject.Id); // returns true
I don't know what the PersonList consist of, but if the elements have, for instance, an Id property than you could do something like this:
var Persons = Data.Where(kvp => !PersonList.Any(person => person.Id == kvp.Key.Id))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can try following code snippet
var result = data.Where(d => PersonList.All(p => p != d.Value));

Sorting values consisting of chars and numbers in an List<Tuple<>>() or asp:ListView

I have an Tuple List and I'm filling this list with all directorys of an folder and bind it to an asp:ListView with this code:
List<string> directoryContent = new List<string>(Directory.GetFileSystemEntries(dirPath);
List<Tuple<string, string>> directoryList = new List<Tuple<string, string>>();
for (int i = 0; i < directoryContent.Count; i++)
{
FileAttributes attr = File.GetAttributes(directoryContent[i]);
if (attr.ToString().Equals("Directory"))
{
String str = directoryContent[i].Remove(0, dirPath.Length);
string count = Directory.GetFiles(directoryContent[i], "*.*", SearchOption.AllDirectories).Length.ToString();
directoryList.Add(new Tuple<string, string>(str, count));
}
}
directoryListView.DataSource = directoryList;
directoryListView.DataBind();
For example the found directories are
12
566
10001
10
templates
files
What would be the best way to sort the List or the ListView in this way? Thank you in advance.
I need to sort the directory that they are sorted in this order:
files
templates
10
12
566
1001
You can use Linq for that.
//test data
List<string> list = new List<string>()
{
"12",
"566",
"10001",
"10",
"templates",
"files"
};
int tempInt;
//filter the numbers from the list and sort
var listNumbers = list.Where(x => int.TryParse(x, out tempInt)).Select(y => Convert.ToInt32(y)).OrderBy(z => z);
//filter the strings from the list and sort
var listStrings = list.Where(x => !int.TryParse(x, out tempInt)).OrderBy(y => y);
//join the two lists again
var orderedList = listStrings.Concat(listNumbers.Select(y => y.ToString())).ToList();
Update for Tuple List
List<Tuple<string, string>> list = new List<Tuple<string, string>>()
{
new Tuple<string, string>("12", "NA"),
new Tuple<string, string>("566", "NA"),
new Tuple<string, string>("10001", "NA"),
new Tuple<string, string>("10", "NA"),
new Tuple<string, string>("templates", "NA"),
new Tuple<string, string>("files", "NA")
};
int tempInt;
//filter the numbers from the list and sort
var listNumbers = list.Where(x => int.TryParse(x.Item1, out tempInt)).Select(y => new Tuple<int, string>(Convert.ToInt32(y.Item1), y.Item2)).OrderBy(z => z.Item1);
//filter the strings from the list and sort
var listStrings = list.Where(x => !int.TryParse(x.Item1, out tempInt)).OrderBy(z => z.Item1);
//join the two lists again
var orderedList = listStrings.Concat(listNumbers.Select(y => new Tuple<string, string>(y.Item1.ToString(), y.Item2))).ToList();

Sort Descending directly

I have
List<string> strs;
double[] values;
where the values array contains the value of each of the string in strs list
Say strs={"abc","def","ghi"}
and values={3,1,2}
this means "abc" has value 3 and so on.
I wish to sort strs and values ordered by values, such that it becomes
strs={"def","ghi","abc"}
values={3,2,1}
I am using
string[] strsArr = strs.ToArray();
Array.Sort(values, strsArr);//1. sort it ascendingly
strs = strsArr.ToList();
Array.Reverse(strs);//2. reverse it
Is there a way I can sort it in descending sequence directly without 2 phases?
You can use a Dictionary and Linq to solve this.
var dict = new Dictionary<string, double>()
{
{"abc",3},
{"def",1},
{"ghi",2}
};
var sorted = dict.OrderByDescending(g => g.Value)
.Select(g => g.Key)
.ToArray();
Note, unless you have a ToArray() at the end the sorting will be deferred till later enumerated and may accidentally be enumerated multiple times.
How about this:
var strs = new [] { "abc", "def", "ghi", };
var values = new [] { 3, 1, 2, };
strs =
strs
.Zip(values, (s, v) => new { s, v })
.OrderByDescending(sv => sv.v)
.Select(sv => sv.s)
.ToArray();
try use dictionary:
Dictionary<string, double> dictionary = new Dictionary<string, double>();
dictionary.Add("abc", 3);
dictionary.Add("def", 1);
dictionary.Add("ghi", 2);
var sortedDict = dictionary.OrderByDescending(x => x.Value);
double[] values = sortedDict.Select(x => x.Value).ToArray();
List<string> strs = sortedDict.Select(x => x.Key).ToList();

create a dictionary using 2 lists using LINQ

I am trying to create a dictionary from 2 lists where one list contains keys and one list contains values. I can do it using for loop but I am trying to find if there is a way of doing it using LINQ.
Sample code will be helpfull. Thanks!!!!
In .NET4 you could use the built-in Zip method to merge the two sequences, followed by a ToDictionary call:
var keys = new List<int> { 1, 2, 3 };
var values = new List<string> { "one", "two", "three" };
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => x.Value);
List<string> keys = new List<string>();
List<string> values = new List<string>();
Dictionary<string, string> dict = keys.ToDictionary(x => x, x => values[keys.IndexOf(x)]);
This of course assumes that the length of each list is the same and that the keys are unique.
UPDATE: This answer is far more efficient and should be used for lists of non-trivial size.
You can include the index in a Select expression to make this efficient:
var a = new List<string>() { "A", "B", "C" };
var b = new List<string>() { "1", "2", "3" };
var c = a.Select((x, i) => new {key = x, value = b[i]}).ToDictionary(e => e.key, e => e.value );
foreach (var d in c)
Console.WriteLine(d.Key + " = " + d.Value);
Console.ReadKey();
var dic = keys.Zip(values, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
You can use this code and working perfectly.
C# Code:
var keys = new List<string> { "Kalu", "Kishan", "Gourav" };
var values = new List<string> { "Singh", "Paneri", "Jain" };
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < keys.Count; i++)
{
dictionary.Add(keys[i].ToString(), values[i].ToString());
}
foreach (var data in dictionary)
{
Console.WriteLine("{0} {1}", data.Key, data.Value);
}
Console.ReadLine();
Output Screen:

Categories