fruits selected = "banana,banana,cherry,kiwi,strawberry"
lookup List<string>
"banana,strawberry" (true)
"strawberry" (true)
"banana,banana,banana" (false)
If there is an exact match in the lookup with just parts of the selected items then the item should be selected in result.
What's best way to do this?
The problem is just converting each item to a bag, and then testing for bag containment:
private IDictionary<string, int> StringToBag(string str)
{
return str.Split(',').GroupBy(s => s).ToDictionary(g => g.Key, g => g.Count());
}
private bool BagContains(IDictionary<string, int> haystack, IDictionary<string, int> needle)
{
return needle.All(kv => haystack.ContainsKey(kv.Key) && kv.Value <= haystack[kv.Key]);
}
var bag = StringToBag("banana,banana,cherry,kiwi,strawberry");
bool contained = BagContains(bag, StringToBag("banana,strawberry"));
If your objects were initialized like this:
string selected = "banana,banana,cherry,kiwi,strawberry";
List<string> lookup = new List<string>()
{
"banana,strawberry",
"strawberry",
"banana,banana,banana"
};
and you had a method for grouping like this:
Dictionary<string, int> ToGroupDictionary(string value)
{
return value.Split(',')
.GroupBy(s => s.Trim())
.ToDictionary(g => g.Key, g => g.Count());
}
you could test each string in lookup like this:
var selectedDictionary = ToGroupDictionary(selected);
// ["banana", 2]
// ["cherry", 1]
// ["kiwi", 1]
// ["strawberry", 1]
foreach(string test in lookup)
{
var testDictionary = ToGroupDictionary(test);
testDictionary.Keys.ToList().ForEach(k =>
Console.WriteLine(selectedDictionary.ContainsKey(k) &&
selectedDictionary[k] >= testDictionary[k]));
// [0] :=
// ["banana", 1]
// ["strawberry", 1]
// true, banana and strawberry exist
// [1] :=
// ["strawberry", 1]
// true, strawberry exists
// [2] :=
// ["banana", 3]
// false, too many bananas
}
public static bool CheckCombination(List<string> values, List<string> combinations)
{
var valuesLookup = values.ToLookup(x => x);
return CheckCombination(valuesLookup, combinations);
}
public static bool CheckCombination(ILookup<string, string> valuesLookup, List<string> combinations)
{
foreach (var combination in combinations.GroupBy(x => x))
{
if (valuesLookup.Contains(combination.Key) &&
valuesLookup[combination.Key].Count() < combination.Count())
return false;
}
return true;
}
Not tested, but will this work:
string fr = "banana,banana,cherry,kiwi,strawberry";
IList<string> selFr = fr.Split(new string[] { "," }, StringSplitOptions.None);
IList<string> look = new List<string>();
// Add the lookup values to the "look" list here...
IList<string> res = new List<string>();
foreach (string lookupStr in look) {
foreach (string f in selFr) {
if (lookupStr.Contains(f)) {
res.Add(lookupStr);
continue;
}
}
}
return res;
Check this out:
private static void LoadFruits(string Fruit, Dictionary<string, int> FruitDictionary)
{
if (FruitDictionary.ContainsKey(Fruit))
FruitDictionary[Fruit] = FruitDictionary[Fruit] + 1;
else
FruitDictionary.Add(Fruit, 1);
}
private static bool HasFruit(string Fruit, Dictionary<string, int> FruitDictionary)
{
if (FruitDictionary.ContainsKey(Fruit) && FruitDictionary[Fruit] > 0)
{
FruitDictionary[Fruit] = FruitDictionary[Fruit] - 1;
return true;
}
return false;
}
...
List<string> AllThefruits = new List<string>(){"banana" ,"banana","cherry","kiwi","strawberry"};
Dictionary<string, int> FruitsDictionary = new Dictionary<string, int>();
List<string> Combination1 = new List<string>() { "banana", "strawberry" };
AllThefruits.ForEach(x => LoadFruits(x, FruitsDictionary));
bool TestCombination1 = Combination1.All(x => HasFruit(x, FruitsDictionary)); //true
FruitsDictionary.Clear();
List<string> Combination2 = new List<string>() { "strawberry" };
AllThefruits.ForEach(x => LoadFruits(x, FruitsDictionary));
bool TestCombination2 = Combination2.All(x => HasFruit(x, FruitsDictionary)); //true
FruitsDictionary.Clear();
List<string> Combination3 = new List<string>() { "banana", "banana", "banana" };
AllThefruits.ForEach(x => LoadFruits(x, FruitsDictionary));
bool TestCombination3 = Combination3.All(x => HasFruit(x, FruitsDictionary)); //false
FruitsDictionary.Clear();
List<string> Combination4 = new List<string>() { "banana", "banana" };
AllThefruits.ForEach(x => LoadFruits(x, FruitsDictionary));
bool TestCombination4 = Combination4.All(x => HasFruit(x, FruitsDictionary)); //true
However, I'm not sure if this is the best solution.
Related
I'm facing an issue while displaying multiple lists the value in a single row column.
Here is an example of code.
public class Program
{
static void Main(string[] args)
{
Dictionary<string, List<object>> keyvalues = new Dictionary<string, List<object>>();
keyvalues.Add("Code", new List<object>() { 1, 2, 3, 4 });
keyvalues.Add("Name", new List<object>() { "A", "B", "C", "D" });
keyvalues.Add("Age", new List<object>() { 20, 30, 40, 50 });
var listData = keyvalues.Select(x => x.Value).Select((x, i) => new { obj = x, index = i });
var listData = keyvalues.Select((x, iparent) => x.Value.Select((z, i) => new { value = string.Concat(z, x.Value[i]) }).ToList()).ToList();
Console.ReadLine();
}
}
Expected output
1A20
2B30
3C40
4D50
If you are using .Net 6, you could make use of the new 3 way Zip extension.
var result = keyvalues["Code"].Zip(keyvalues["Name"], keyvalues["Age"])
.Select(x=> $"{x.First}{x.Second}{x.Third}");
Why make it so complicated?
for(int x = 0; x<keyValues["Code"].Count; x++)
Console.WriteLine(
keyValues["Code"][x]+
keyValues["Name"][x]+
keyValues["Age"][x]
);
LINQ's a hammer; not every problem is a nail.
ps if you have N keys, you can easily turn it into a
var keys = new[]{"Code","Name","Age","Foo","Bar"};
for(...)
foreach(var k in keys)
... //some concat here or use the values directly eg adding to your page
You could easily use Zip here. However, you could roll your own
public static IEnumerable<string> DoStuff<T, T2>(Dictionary<T, List<T2>> source)
{
var max = source.Values.Max(x => x?.Count ?? 0);
for (var i = 0; i < max; i++)
yield return string.Concat(source.Values.Select(x => x.ElementAtOrDefault(i)));
}
Usage
var results = DoStuff(keyvalues);
Console.WriteLine(string.Join(Environment.NewLine,results));
Output
1A20
2B30
3C40
4D50
or
public static IEnumerable<string> DoStuff<T>(List<T>[] source)
{
var max = source.Max(x => x?.Count ?? 0);
for (var i = 0; i < max; i++)
yield return string.Concat(source.Select(x => x.ElementAtOrDefault(i)));
}
...
var results = DoStuff(keyvalues.Values.ToArray());
Console.WriteLine(string.Join(Environment.NewLine,results));
I am looking for the best algorithm to compare 2 collections and determine which element got added and which element got removed.
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's for you to know what useful data is inside UserInvolvement.
//Both are Dictionary<int, int>, because The Involvement is just a enum flag. Integer. UserId is also Integer.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
//I Want to compare new to old -> and get 2 dictionaries: added and removed.
var usersAdded = new Dictionary<int, Involvement>();
var usersRemoved = new Dictionary<int, Involvement>();
//What is the best algoritm to accomplish this?
return GetInvolvementLogging(usersAdded, usersRemoved);
}
private string GetInvolvementLogging(Dictionary<int, Involvement> usersAdded, Dictionary<int, Involvement> usersRemoved)
{
//TODO: generate a string based on those dictionaries.
return "Change in userinvolvement: ";
}
Added elements are only in newDict removed only in oldDict
var intersection = newDict.Keys.Intersect(oldDict.Keys);
var added = newDict.Keys.Except(intersection);
var removed = oldDict.Keys.Except(intersection);
EDIT
I modify your base function, dictionaries is no neded.
Example UserInvolvement implementation
class UserInvolvement
{
public int UserId;
public string Name;
public string OtherInfo;
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
UserInvolvement p = obj as UserInvolvement;
if ((System.Object)p == null)
{
return false;
}
return (UserId == p.UserId) && (Name == p.Name) && (OtherInfo == p.OtherInfo);
}
public override string ToString()
{
return $"{UserId} - {Name} - {OtherInfo}";
}
}
And example function:
private static string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement,
ICollection<UserInvolvement> oldInvolvement)
{
var intersection = newInvolvement.Select(x => x.UserId).Intersect(oldInvolvement.Select(x => x.UserId));
var addedIds = newInvolvement.Select(x => x.UserId).Except(intersection);
var removedIds = oldInvolvement.Select(x => x.UserId).Except(intersection);
List<UserInvolvement> modifiedUI = new List<UserInvolvement>();
foreach (var i in intersection)
{
var ni = newInvolvement.First(a => a.UserId == i);
var oi = oldInvolvement.First(a => a.UserId == i);
if (!ni.Equals(oi))
{
modifiedUI.Add(ni);
}
}
List<UserInvolvement> addedUI = newInvolvement.Where(x => addedIds.Contains(x.UserId)).Select(w => w).ToList();
List<UserInvolvement> removedUI = oldInvolvement.Where(x => removedIds.Contains(x.UserId)).Select(w => w).ToList();
StringBuilder sb = new StringBuilder();
sb.AppendLine("Added");
foreach (var added in addedUI)
{
sb.AppendLine(added.ToString());
}
sb.AppendLine("Removed");
foreach (var removed in removedUI)
{
sb.AppendLine(removed.ToString());
}
sb.AppendLine("Modified");
foreach (var modified in modifiedUI)
{
sb.AppendLine(modified.ToString());
}
return sb.ToString();
}
And my test function:
static void Main(string[] args)
{
List<UserInvolvement> newUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 1,
Name = "AAA",
OtherInfo = "QQQ"
},
new UserInvolvement()
{
UserId = 2,
Name = "BBB",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
List<UserInvolvement> oldUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 2,
Name = "BBBC",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 3,
Name = "CCC",
OtherInfo = "QQ44"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
string resp = GetInvolvementLogging(newUI, oldUI);
WriteLine(resp);
ReadKey();
WriteLine("CU");
}
Result is:
Added
1 - AAA - QQQ
Removed
3 - CCC - QQ44
Modified
2 - BBB - 123
You could try with Linq:
var usersAdded = newDict.Except(oldDict);
var usersRemoved = oldDict.Except(newDict);
If you need dictionaries as a result you can cast:
var usersAdded = newDict.Except(oldDict).ToDictionary(x => x.Key, x => x.Value);
var usersRemoved = oldDict.Except(newDict).ToDictionary(x => x.Key, x => x.Value);
Think best algorithm will be
foreach (var newItem in newDict)
if (!oldDict.ContainsKey(newItem.Key) || oldDict[newItem.Key]!=newItem.Value)
usersAdded.Add(newItem.Key, newItem.Value);
foreach (var oldItem in oldDict)
if (!newDict.ContainsKey(oldItem.Key) || newDict[oldItem.Key]!=oldItem.Value)
usersRemoved.Add(oldItem.Key, oldItem.Value);
Finally this is my implementation of GetInvolvementLogging:
(the implementation of the string builder method is irrelevant for my question here)
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's to focus on the relevant data inside UserInvolvement.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var intersection = newDict.Keys.Intersect(oldDict.Keys); //These are the id's of the users that were and remain involved.
var usersAdded = newDict.Keys.Except(intersection);
var usersRemoved = oldDict.Keys.Except(intersection);
var addedInvolvement = newDict.Where(x => usersAdded.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
var removedInvolvement = oldDict.Where(x => usersRemoved.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
//Check if the already involved users have a changed involvement.
foreach(var userId in intersection)
{
var newInvolvementFlags = newDict[userId];
var oldInvolvementFlags = oldDict[userId];
if ((int)newInvolvementFlags != (int)oldInvolvementFlags)
{
var xor = newInvolvementFlags ^ oldInvolvementFlags;
var added = newInvolvementFlags & xor;
var removed = oldInvolvementFlags & xor;
if (added != 0)
{
addedInvolvement.Add(userId, added);
}
if (removed != 0)
{
removedInvolvement.Add(userId, removed);
}
}
}
return GetInvolvementLogging(addedInvolvement, removedInvolvement);
}
I'm new to C# and need some help with comparing collections. I have two List<string>
collections with their contents as below:
Collection Old:
{"AAA","BBB","CCC"}
Collection New:
{"BBB","CCC","DDD"}
I want to get a collection like below:
Collection Final:
{"AAA", "Remove"; "BBB", "Keep"; "CCC", "Keep"; "DDD", "Add"}
How can I do this?
old.Except(new) will give you those items to remove
new.Except(old) will give you items to add
old.Intersect(new) will give you items to keep
(This is assuming you don't mind using the System.Linq namespace)
Or if you prefer, you can consider each item individually and check the existence in each list
var oldList = new List<String>() {"AAA", "BBB", "CCC"};
var newList = new List<String>() {"BBB", "CCC", "DDD"};
var diffDictionary = new Dictionary<string, string>();
foreach (var oldEntry in oldList)
{
diffDictionary.Add(oldEntry, "Remove");
}
foreach (var newEntry in newList)
{
if (diffDictionary.ContainsKey(newEntry))
{
diffDictionary[newEntry] = "Keep";
}
else
{
diffDictionary.Add(newEntry, "Add");
}
}
foreach (var dDico in diffDictionary)
{
Console.WriteLine(string.Concat("Key: ", dDico.Key, " Value: ", dDico.Value));
}
You can use a dictionary to do this...
at the end, each element in the dictionary will tell you how many items of each kind were removed or added.
It will indicate this with a count, not a simple 3 state flag... that is because you may have added or removed repeated items... what if you insert 3 AAA's in the second collection.
string[] col1 = new string[] { "AAA", "BBB", "CCC" };
string[] col2 = new string[] { "BBB", "CCC", "DDD" };
Dictionary<string, int> colDic = new Dictionary<string, int>();
foreach (var item in col1)
{
int num;
if (colDic.TryGetValue(item, out num))
colDic[item] = num - 1;
else
colDic[item] = -1;
}
foreach (var item in col2)
{
int num;
if (colDic.TryGetValue(item, out num))
colDic[item] = num + 1;
else
colDic[item] = 1;
}
The end result will look like this:
AAA = -1
BBB = 0
CCC = 0
DDD = 1
In 1 line (sort of)!
string[] colOld = {"AAA","BBB","CCC"};
string[] colNew = {"BBB","CCC","DDD"};
dynamic colALL = (from o in colOld.Union(colNew)
select new {Value = o, Action =
colOld.Any(s => s == o) ?
colNew.Any(s => s == o) ? "Keep" : "Remove"
: "Add"
}).ToList();
Note: This is a developer fusion conversionof the below vb.net which does work - I've not had chance to test the c# version:
Dim colOld() As String = {"AAA", "BBB", "CCC"}
Dim colNew() As String = {"BBB", "CCC", "DDD"}
Dim colALL = (From o As String In colOld.Union(colNew) _
Select New With {.Value = o, .Action = _
If(colOld.Any(Function(s) s = o), _
If(colNew.Any(Function(s) s = o), "Keep", "Remove"), _
"Add")}).ToList
If you have this method
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences)
{
return sequences.SelectMany(x => x);
}
you should be able to write:
static readonly string Remove = "Remove";
static readonly string Keep = "Keep";
static readonly string Add = "Add";
var result = Concat
(
old.Except(new).Select(x => new { x, Remove }),
old.Intersect(new).Select(x => new { x, Keep }),
new.Except(old).Select(x => new { x, Add })
);
Of course you can use the built-in Enumerable.Concat method but I find mine more elegant.
This question already has answers here:
C# LINQ find duplicates in List
(13 answers)
Closed 3 years ago.
I have a List<string> which has some words duplicated. I need to find all words which are duplicates.
Any trick to get them all?
In .NET framework 3.5 and above you can use Enumerable.GroupBy which returns an enumerable of enumerables of duplicate keys, and then filter out any of the enumerables that have a Count of <=1, then select their keys to get back down to a single enumerable:
var duplicateKeys = list.GroupBy(x => x)
.Where(group => group.Count() > 1)
.Select(group => group.Key);
If you are using LINQ, you can use the following query:
var duplicateItems = from x in list
group x by x into grouped
where grouped.Count() > 1
select grouped.Key;
or, if you prefer it without the syntactic sugar:
var duplicateItems = list.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x.Key);
This groups all elements that are the same, and then filters to only those groups with more than one element. Finally it selects just the key from those groups as you don't need the count.
If you're prefer not to use LINQ, you can use this extension method:
public void SomeMethod {
var duplicateItems = list.GetDuplicates();
…
}
public static IEnumerable<T> GetDuplicates<T>(this IEnumerable<T> source) {
HashSet<T> itemsSeen = new HashSet<T>();
HashSet<T> itemsYielded = new HashSet<T>();
foreach (T item in source) {
if (!itemsSeen.Add(item)) {
if (itemsYielded.Add(item)) {
yield return item;
}
}
}
}
This keeps track of items it has seen and yielded. If it hasn't seen an item before, it adds it to the list of seen items, otherwise it ignores it. If it hasn't yielded an item before, it yields it, otherwise it ignores it.
and without the LINQ:
string[] ss = {"1","1","1"};
var myList = new List<string>();
var duplicates = new List<string>();
foreach (var s in ss)
{
if (!myList.Contains(s))
myList.Add(s);
else
duplicates.Add(s);
}
// show list without duplicates
foreach (var s in myList)
Console.WriteLine(s);
// show duplicates list
foreach (var s in duplicates)
Console.WriteLine(s);
If you're looking for a more generic method:
public static List<U> FindDuplicates<T, U>(this List<T> list, Func<T, U> keySelector)
{
return list.GroupBy(keySelector)
.Where(group => group.Count() > 1)
.Select(group => group.Key).ToList();
}
EDIT: Here's an example:
public class Person {
public string Name {get;set;}
public int Age {get;set;}
}
List<Person> list = new List<Person>() { new Person() { Name = "John", Age = 22 }, new Person() { Name = "John", Age = 30 }, new Person() { Name = "Jack", Age = 30 } };
var duplicateNames = list.FindDuplicates(p => p.Name);
var duplicateAges = list.FindDuplicates(p => p.Age);
foreach(var dupName in duplicateNames) {
Console.WriteLine(dupName); // Will print out John
}
foreach(var dupAge in duplicateAges) {
Console.WriteLine(dupAge); // Will print out 30
}
Using LINQ, ofcourse.
The below code would give you dictionary of item as string, and the count of each item in your sourc list.
var item2ItemCount = list.GroupBy(item => item).ToDictionary(x=>x.Key,x=>x.Count());
For what it's worth, here is my way:
List<string> list = new List<string>(new string[] { "cat", "Dog", "parrot", "dog", "parrot", "goat", "parrot", "horse", "goat" });
Dictionary<string, int> wordCount = new Dictionary<string, int>();
//count them all:
list.ForEach(word =>
{
string key = word.ToLower();
if (!wordCount.ContainsKey(key))
wordCount.Add(key, 0);
wordCount[key]++;
});
//remove words appearing only once:
wordCount.Keys.ToList().FindAll(word => wordCount[word] == 1).ForEach(key => wordCount.Remove(key));
Console.WriteLine(string.Format("Found {0} duplicates in the list:", wordCount.Count));
wordCount.Keys.ToList().ForEach(key => Console.WriteLine(string.Format("{0} appears {1} times", key, wordCount[key])));
I'm assuming each string in your list contains several words, let me know if that's incorrect.
List<string> list = File.RealAllLines("foobar.txt").ToList();
var words = from line in list
from word in line.Split(new[] { ' ', ';', ',', '.', ':', '(', ')' }, StringSplitOptions.RemoveEmptyEntries)
select word;
var duplicateWords = from w in words
group w by w.ToLower() into g
where g.Count() > 1
select new
{
Word = g.Key,
Count = g.Count()
}
I use a method like that to check duplicated entrys in a string:
public static IEnumerable<string> CheckForDuplicated(IEnumerable<string> listString)
{
List<string> duplicateKeys = new List<string>();
List<string> notDuplicateKeys = new List<string>();
foreach (var text in listString)
{
if (notDuplicateKeys.Contains(text))
{
duplicateKeys.Add(text);
}
else
{
notDuplicateKeys.Add(text);
}
}
return duplicateKeys;
}
Maybe it's not the most shorted or elegant way, but I think that is very readable.
lblrepeated.Text = "";
string value = txtInput.Text;
char[] arr = value.ToCharArray();
char[] crr=new char[1];
int count1 = 0;
for (int i = 0; i < arr.Length; i++)
{
int count = 0;
char letter=arr[i];
for (int j = 0; j < arr.Length; j++)
{
char letter3 = arr[j];
if (letter == letter3)
{
count++;
}
}
if (count1 < count)
{
Array.Resize<char>(ref crr,0);
int count2 = 0;
for(int l = 0;l < crr.Length;l++)
{
if (crr[l] == letter)
count2++;
}
if (count2 == 0)
{
Array.Resize<char>(ref crr, crr.Length + 1);
crr[crr.Length-1] = letter;
}
count1 = count;
}
else if (count1 == count)
{
int count2 = 0;
for (int l = 0; l < crr.Length; l++)
{
if (crr[l] == letter)
count2++;
}
if (count2 == 0)
{
Array.Resize<char>(ref crr, crr.Length + 1);
crr[crr.Length - 1] = letter;
}
count1 = count;
}
}
for (int k = 0; k < crr.Length; k++)
lblrepeated.Text = lblrepeated.Text + crr[k] + count1.ToString();
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);
}