create a dictionary using 2 lists using LINQ - c#

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:

Related

Two arrays into one Dictionary

I would like to create a
Dictionary<string, int[]> dict
out of two arrays:
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
the result:
["A"] = {1,5}
["B"] = {2}
["D"] = {2}
Is there a way i can do this with LINQ?
I have read about Zip but I don't think I can use since I need to add values to an existing key.value array.
Use .Zip to bind the two collections together and then GroupBy to group the keys.
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
var result = keys.Zip(values, (k, v) => new { k, v })
.GroupBy(item => item.k, selection => selection.v)
.ToDictionary(key => key.Key, value => value.ToArray());
Then to add these items into the dictionary that you already have:
I changed the int[] to List<int> so it is easier to handle Add/AddRange
Dictionary<string, List<int>> existingDictionary = new Dictionary<string, List<int>>();
foreach (var item in result)
{
if (existingDictionary.ContainsKey(item.Key))
existingDictionary[item.Key].AddRange(item.Value);
else
existingDictionary.Add(item.Key, item.Value.ToList());
}
Linq solution:
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
Dictionary<string, int[]> dict = keys
.Zip(values, (k, v) => new {
key = k,
value = v })
.GroupBy(pair => pair.key, pair => pair.value)
.ToDictionary(chunk => chunk.Key,
chunk => chunk.ToArray());
Test:
string report = String.Join(Environment.NewLine, dict
.Select(pair => $"{pair.Key} [{string.Join(", ", pair.Value)}]"));
Console.Write(report);
Outcome:
A [1, 5]
B [2]
D [2]
Try this :
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
Dictionary<string, int[]> dict = keys.Select((x, i) => new { key = x, value = values[i] }).GroupBy(x => x.key, y => y.value).ToDictionary(x => x.Key, y => y.ToArray());

Linq SelectMany project to index

I have 2 different collections.
pseudo code:
// index string by int : Dictionary<int, string>
index = { 0, "a" }, { 1, "b" }, { 2, "c" }
// data : Dictionary<string, List<Data>>
data = {"a", { "data00", "data01"..}},
{"b", {"data20", "data21", "data22"...}},
{"c", {"data4",...}}...
I want project int index to data string value and
var result = data.SelectMany ... new { IntIndex, DataValue }
I need to flatten lists into one sequence and pair the Data values with int index using string index.
I have slightly update types and values (your Dictionary contains duplicated keys and index wasn't specified) but it shouldn't be a problem. You may modify function easily for your data types.
var index = new List<Tuple<int, string>> {Tuple.Create(0, "a"), Tuple.Create(1, "b")};
var data = new Dictionary<string, IEnumerable<string>>()
{
{"a", new[] {"data00", "data01"}},
{"b", new[] {"data20", "data21", "data22"}},
{"c", new[] {"data4"}}
};
var result = index
.Join(data, x => x.Item2, y => y.Key, (x,y) => new KeyValuePair<int, IEnumerable<string>>(x.Item1, y.Value))
.SelectMany(x => x.Value, (x, y) => new KeyValuePair<int, string>(x.Key, y));
Assuming your duplication of keys was accidental, you could try this
Dictionary<int, List<Data>> intData = new Dictionary<int, List<Data>>();
foreach (var iVal in index)
{
List<Data> tmpList = new List<Data>();
if (data.TryGetValue(iVal.Value, out tmpList))
{
intData.Add(iVal.Key, tmpList);
}
}
If you can have duplicate keys then a dictionary is not the right structure.
var index = new List<Tuple<int, string>> {Tuple.Create(0, "a"), Tuple.Create(1, "b")};
var data = new Dictionary<string, IEnumerable<string>>()
{
{"a", new[] {"data00", "data01"}},
{"b", new[] {"data20", "data21", "data22"}},
{"c", new[] {"data4"}}
};
var res =
(from i in index
join d in data on i.Item2 equals d.Key
select new {Key = i.Item1, Value = d.Value})
.SelectMany(x => x.Value, (x, v) => new {x.Key, Value = v});

Manipulation on all list items options for n lists in c#

I'd like to split my data to lists by an attribute's value and check all the combination options between the lists' items.
My problems are that I don't know how many list I'll get and if there is a better way to do that beside this way:
var a = Data.Descendants("value").Where(x => x.Attribute("v").Value == "1").ToList();
var b = Data.Descendants("value").Where(x => x.Attribute("v").Value == "2").ToList();
var c = Data.Descendants("value").Where(x => x.Attribute("v").Value == "3").ToList();
foreach (var tempA in a)
{
foreach (var tempB in b)
{
foreach (var tempC in c)
{
DO SOMETHING;
}
}
}
EDIT:
I'd like to check my items from one data source (var items = new List<string>{"1","1","2","3","2","1","3","3","2"})
Now I'd like to split this list to 3 lists (list a = "1","1","1" - list b = "2","2","2" - list c = "3","3","3")
In this step what I'm trying to do is to check all the combination from one item in one list to the other items in the other lists.
a[0] with b[0] c[0]
a[0] with b[0] c[1]
a[0] with b[0] c[2]
a[0] with b[1] c[0]
.
.
b[1] with a[2] c[2]
.
.
Thanks!
Could you try using the LINQ GroupBy method? Some examples are here:
LINQ GroupBy examples
You can use GroupBy to group your elements. Then you can create combinations using Linq.
var grouping = Data.Descendants("value")
.GroupBy(x => x.Attribute("v").Value);
var combinations grouping.SelectMany(x =>
grouping.Select(y =>
new { Group = x, Combination = y }));
foreach(var c in combinations)
{
//Do Something
}
e.g.
public class Pair
{
public string A { get; set; }
public string B { get; set; }
}
var pairs = new List<Pair>();
pairs.Add(new Pair { A = "1", B = "2" });
pairs.Add(new Pair { A = "1", B = "3" });
pairs.Add(new Pair { A = "1", B = "4" });
pairs.Add(new Pair { A = "2", B = "1" });
pairs.Add(new Pair { A = "2", B = "2" });
pairs.Add(new Pair { A = "2", B = "3" });
var grouping = pairs.GroupBy(x => x.A);
var combinations = grouping.SelectMany(x =>
grouping.Select(y =>
new { Group = x, Combination = y }));
You can do this,following the line of thinking of romoku and chrisC
//new list of lists to hold new information.
List<List<Descendants>> NewList = new List<List<Descendants>>();
foreach (var item in Data.Descendants.GroupBy(x => x.Attribute("v").Value))
{
NewList.Add(item.ToList());
}
For your new edit list of Strings this will do it
List<List<string>> NewList = new List<List<string>>();
foreach (var item in OriginalList.GroupBy(x => x))
{
NewList.Add(item.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();

C# : Merging Dictionary and List

I have a List of String like
List<String> MyList=new List<String>{"A","B"};
and a
Dictionary<String, Dictionary<String,String>> MyDict=new Dictionary<String,Dictionary<String,String>>();
which contains
Key Value
Key Value
"ONE" "A_1" "1"
"A_2" "2"
"X_1" "3"
"X_2" "4"
"B_1" "5"
"TWO" "Y_1" "1"
"B_9" "2"
"A_4" "3"
"B_2" "6"
"X_3" "7"
I need to merge the the list and Dictionary into a new Dictionary
Dictionary<String,String> ResultDict = new Dictionary<String,String>()
The resulting dictionary contains
Key Value
"A_1" "1"
"A_2" "2"
"B_1" "5"
"A_4" "3"
"B_2" "6"
"X_2" "4"
"X_3" "7"
Merge rule
First add the items which has a substring equals to any item in the list.
Then Merge the items in the "MyDict" so the result should not contain duplicate keys as well as duplicate values.
Here is my source code.
Dictionary<String, String> ResultDict = new Dictionary<string, string>();
List<String> TempList = new List<string>(MyDict.Keys);
for (int i = 0; i < TempList.Count; i++)
{
ResultDict = ResultDict.Concat(MyDict[TempList[i]])
.Where(TEMP => MyList.Contains(TEMP.Key.Contains('_') == true ? TEMP.Key.Substring(0, TEMP.Key.LastIndexOf('_'))
: TEMP.Key.Trim()))
.ToLookup(TEMP => TEMP.Key, TEMP => TEMP.Value)
.ToDictionary(TEMP => TEMP.Key, TEMP => TEMP.First())
.GroupBy(pair => pair.Value)
.Select(group => group.First())
.ToDictionary(pair => pair.Key, pair => pair.Value); }
for (int i = 0; i < TempList.Count; i++)
{
ResultDict = ResultDict.Concat(MyDict[TempList[i]])
.ToLookup(TEMP => TEMP.Key, TEMP => TEMP.Value)
.ToDictionary(TEMP => TEMP.Key, TEMP => TEMP.First())
.GroupBy(pair => pair.Value)
.Select(group => group.First())
.ToDictionary(pair => pair.Key, pair => pair.Value);
}
its working fine, but I need to eliminate the two for loops or at least one
(Any way to do this using LINQ or LAMBDA expression)
Here's one way you could do it with LINQ and lambdas, as requested:
var keysFromList = new HashSet<string>(MyList);
var results =
MyDict.Values
.SelectMany(x => x)
.OrderBy(x => {
int i = x.Key.LastIndexOf('_');
string k = (i < 0) ? x.Key.Trim()
: x.Key.Substring(0, i);
return keysFromList.Contains(k) ? 0 : 1;
})
.Aggregate(new {
Results = new Dictionary<string, string>(),
Values = new HashSet<string>()
},
(a, x) => {
if (!a.Results.ContainsKey(x.Key)
&& !a.Values.Contains(x.Value))
{
a.Results.Add(x.Key, x.Value);
a.Values.Add(x.Value);
}
return a;
},
a => a.Results);
Loop wise this code is simpler, but not Linq:
public static Dictionary<string, string> Test()
{
int initcount = _myDict.Sum(keyValuePair => keyValuePair.Value.Count);
var usedValues = new Dictionary<string, string>(initcount); //reverse val/key
var result = new Dictionary<string, string>(initcount);
foreach (KeyValuePair<string, Dictionary<string, string>> internalDicts in _myDict)
{
foreach (KeyValuePair<string, string> valuePair in internalDicts.Value)
{
bool add = false;
if (KeyInList(_myList, valuePair.Key))
{
string removeKey;
if (usedValues.TryGetValue(valuePair.Value, out removeKey))
{
if (KeyInList(_myList, removeKey)) continue;
result.Remove(removeKey);
}
usedValues.Remove(valuePair.Value);
add = true;
}
if (!add && usedValues.ContainsKey(valuePair.Value)) continue;
result[valuePair.Key] = valuePair.Value;
usedValues[valuePair.Value] = valuePair.Key;
}
}
return result;
}
private static bool KeyInList(List<string> myList, string subKey)
{
string key = subKey.Substring(0, subKey.LastIndexOf('_'));
return myList.Contains(key);
}

Categories