Insert new Dictionary entries of <UserStatus (enum), IEnumerable<string>> into different keys - c#

we are doing the following programming exercise: Who's Online.
We have written the following code:
using System;
using System.Collections.Generic;
public static class Kata
{
public static Dictionary<UserStatus, IEnumerable<string>> WhosOnline /*❓❓*/ (User[] friends)
{
var users = new Dictionary<UserStatus, IEnumerable<string>>(); //We will save each friends' status and name
for(int i=0; i<friends.Length; i++)
{
Console.WriteLine(friends[i].Username+","+friends[i].Status+","+friends[i].LastActivity);
if(friends[i].Status==UserStatus.Online && friends[i].LastActivity>10){ //If it was active more than 10 minutes ago, it is away
friends[i].Status=UserStatus.Away;
}
users.Add(friends[i].Status, new []{friends[i].Username}); //We need to store the userStatus and its name
//users[friends[i].Status] = new []{friends[i].Username};
}
return users;
}
}
Being the tests:
namespace Solution
{
using NUnit.Framework;
using System;
using System.Collections.Generic;
[TestFixture]
public class SampleTest
{
[Test, Description("Example test two of each")]
public void SoloTest()
{
User[] friends = new User[]
{
new User("David", UserStatus.Online, 10),
new User("Anna", UserStatus.Online, 1),
new User("Lucy", UserStatus.Offline, 22),
new User("Viviana", UserStatus.Offline, 44),
new User("Bob", UserStatus.Online, 104),
new User("Fina", UserStatus.Online, 300),
};
var expected = new Dictionary<UserStatus, IEnumerable<string>>
{
{UserStatus.Online, new[] {"David"}},
{UserStatus.Online, new[] {"Anna"}},
{UserStatus.Offline, new[] {"Lucy"}},
{UserStatus.Offline, new[] {"Viviana"}},
{UserStatus.Away, new[] {"Bob"}},
{UserStatus.Away, new[] {"Fina"}}
};
Assert.That(Kata.WhosOnline(friends), Is.EqualTo(expected));
}
[Test, Description("Example test no one online")]
public void NoOnlineTest()
{
User[] friends = new User[]
{
new User("Lucy", UserStatus.Offline, 22),
new User("Bob", UserStatus.Online, 104)
};
var expected = new Dictionary<UserStatus, IEnumerable<string>>
{
{UserStatus.Offline, new[] {"Lucy"}},
{UserStatus.Away, new[] {"Bob"}}
};
Assert.That(Kata.WhosOnline(friends), Is.EqualTo(expected));
}
}
}
We found that it outputs:
System.ArgumentException : An item with the same key has already been added. Key: Online
We have identified that it is produced because of in the line 16:
users.Add(friends[i].Status, new []{friends[i].Username}); //We need to store the userStatus and its name
We are trying to assign a Dictionary entry, which key has been already inserted.
We would need to do something like the following:
users.Add(i, {friends[i].Status, new []{friends[i].Username}}); //We need to store the userStatus and its name
Adding to the dictionary each entry with a unique key. However if we execute the code with the previous line we get:
src/Solution.cs(17,20): error CS1525: Invalid expression term '{'
And if we execute it as follows:
users.Add(i, friends[i].Status, new []{friends[i].Username}); //We need to store the userStatus and its name
We observe:
src/Solution.cs(17,13): error CS1501: No overload for method 'Add' takes 3 arguments
In addition we also have tried to store entries like:
users[friends[i].Status] = new []{friends[i].Username};
However, it produces the same output:
System.ArgumentException : An item with the same key has already been added. Key: Online
Besides, we have adapted it as:
users[i] = (friends[i].Status,new []{friends[i].Username});
After that, we get:
src/Solution.cs(20,13): error CS1503: Argument 1: cannot convert from 'int' to 'UserStatus'
How could we insert new entries of UserStatus, IEnumerable, with unique keys?
We have read:
printing all contents of array in C#
Different ways of adding to Dictionary
Dictionary with object as value
C# appending Dictionary
Adding an item to a dictionary as the first item c#
Adding a dictionary element at a specific place
How to insert element in first index in dictionary?
C# Add elements to Dictionary with key as string and value as another Dictionary
EDIT: Thanks to #Guru Stron we have written the following code:
using System;
using System.Collections.Generic;
public static class Kata
{
public static Dictionary<UserStatus, IEnumerable<string>> WhosOnline /*❓❓*/ (User[] friends)
{
var users = new Dictionary<UserStatus, List<string>>(); //We will save each friends' status and name
for(int i=0; i<friends.Length; i++)
{
Console.WriteLine(friends[i].Username+","+friends[i].Status+","+friends[i].LastActivity);
if(friends[i].Status==UserStatus.Online && friends[i].LastActivity>10){ //If it was active more than 10 minutes ago, it is away
friends[i].Status=UserStatus.Away;
}
if(!users.ContainsKey(friends[i].Status)){ //-> We should check if this status has already been saved
users.Add(friends[i].Status, new List<string>{friends[i].Username});
}else{
users[friends[i].Status].Add(friends[i].Username);
}
}
return users;
}
}
However console writes:
src/Solution.cs(25,12): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.Dictionary<UserStatus, System.Collections.Generic.List<string>>' to 'System.Collections.Generic.Dictionary<UserStatus, System.Collections.Generic.IEnumerable<string>>'
Because of we are trying to save a List inside a dictionary which has been declared as IEnumerable, and we can not change its type.
Then we tried, to convert it back to IEnumerable
using System;
using System.Collections.Generic;
public static class Kata
{
public static Dictionary<UserStatus, IEnumerable<string>> WhosOnline /*❓❓*/ (User[] friends)
{
var users = new Dictionary<UserStatus, List<string>>(); //We will save each friends' status and name
for(int i=0; i<friends.Length; i++)
{
Console.WriteLine(friends[i].Username+","+friends[i].Status+","+friends[i].LastActivity);
if(friends[i].Status==UserStatus.Online && friends[i].LastActivity>10){ //If it was active more than 10 minutes ago, it is away
friends[i].Status=UserStatus.Away;
}
if(!users.ContainsKey(friends[i].Status)){
users.Add(friends[i].Status, new List<string>{friends[i].Username});
}else{
users[friends[i].Status].Add(friends[i].Username);
}
}
var output = new Dictionary<UserStatus, IEnumerable<string>>(); //We try to convert List<string> back to IEnumerable<string>
foreach(KeyValuePair<UserStatus, List<string>> entry in users){
if(!output.ContainsKey(entry.Key)){
output.Add(entry.Key, entry.Value.ToArray());
}else{
users[entry.Key].Add(entry.Value.ToArray());
}
}
return output;
}
}
And we observe:
src/Solution.cs(29,30): error CS1503: Argument 1: cannot convert from 'string[]' to 'string'
After that, we followed the suggestion made by #Guru Stron and we started to use Linq as follows:
using System;
using System.Collections.Generic;
using System.Linq;
public static class Kata
{
public static Dictionary<UserStatus, IEnumerable<string>> WhosOnline /*❓❓*/ (User[] friends)
{
/*How could we express: if(f.Status==Online && f.LastActivity > 10) f.Status=Away ❗❓*/
var users = friends.GroupBy(f => f.Status).ToDictionary(g => g.Key,
g => (IEnumerable<string>)(g.Select(f => f.Username).ToList()));
foreach(KeyValuePair<UserStatus, IEnumerable<string>> entry in users)
{
Console.WriteLine("Status: "+entry.Key+" Username: ");
Console.WriteLine("[{0}]",string.Join(", ",entry.Value));
}
return users;
}
}
However, the tests fail because of we would need to express:
if(f.Status==Online && f.LastActivity > 10) f.Status=Away
How could do it with Linq?

Dictionary in C# is collection of unique keys and values associated with them.
So first of all you will want Dictionary<UserStatus, List<string>>() not Dictionary<UserStatus, IEnumerable<string>>()(for convenience). Secondary when adding a user to status group(key) you need to check if such group(key) exists and if yes - add to it, if not - add it:
if(!users.ContainsKey(friends[i].Status))
{
users.Add(friends[i].Status, new List<string>{friends[i].Username})
}
else
{
users[friends[i].Status].Add(friends[i].Username);
}
and lastly you can use LINQ:
var users = friends.GroupBy(f => f.Status)
.ToDictionary(g => g.Key, g => g.Select(f => f.UserName).ToList())
If you can't change signature of the your function LINQ would be more convenient cause you can do
var users = friends.GroupBy(f => f.Status)
.ToDictionary(g => g.Key,
g => (IEnumerable<string>)(g.Select(f => f.UserName).ToList()))

Related

How to use dictionaries with lists as value c# unity

I am using a dictionary system to store my data and each thing I want to add to my dictionary will need to have 8 definitions. I want to know if anything like that is possible and if yes, I want to learn the codes for adding new items to the dictionary and read those items (if possible read the spesific things from the 8 items inside each list using index)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dictionary_matches : MonoBehaviour
{
/*public static Dictionary<string, List<String>> Matches_dic = new Dictionary<string, List<String>>();
dict.Add("key1", new List<int> { 1, 2, 3 });*/
public static Dictionary<string, string> myDict = new Dictionary<string, string>();
myDict.Add("Australia", "Canberra");
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Any help would be appreciated.
It seems, that you are looking for something like this:
public class Dictionary_matches : MonoBehaviour {
public static readonly Dictionary<string, List<string>> Matches_dic =
new Dictionary<string, List<string>>() {
{"match_1", new List<string>() {"a", "b", "c"}},
{"match_2", new List<string>() {"abracadabra"}},
};
public void Update() {
// You can use in any method of the class, e.g.
Matches_dic.Add("more_match", new List<string>() {"p", "q"});
}
// Other staff
}
here we declare the dictionary and with two entries (match_1 and match_2)
When you declare such a dictionary its keys and values are empty.
Let's say I create one such dictionary
var myDict = new Dictionary<string, List<string>>()
Now I want to add a value. But remember, my value is a List<T>.
So you need to check if your key is already present, and if it is already have an instanciated list as a value (e.g. a value != null)
if(myDict.ContainsKey("myKey") && myDict["myKey"] != null)
{
// In this case you could do
mydict["myKey"].Add("myValue");
// Because you already have a list.
// And to read the first item from the list
var firstValue = mydict["myKey"][0]; // or using Linq replace [0] by First()
}
// Now if the key does not exist you need to add the key, and ideally an instantiated list.
if(!myDict.ContainsKey["myKey"])
{
// In your case we know the expected length of the list, so let's specify it.
myDict.Add("myKey", new List<string>(8))
}
You'll notice I didn't write the case where the key would exist, but the value is null.
I'll leave to your consideration if handling that particular case is needed, and if it is, you should have all necessary information in the example above.

How to get data that is already present in input.txt file and find the highest salary using a dictionary C# [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Highest Salary in Dept
I have to read data from given below data from input file
The input is being read in from a file called input.txt in this format:
22,Rajan Anand,Engineering,1600000
23,Swati Patil,Testing,800000
27,Vijay Chawda,Engineering,800000
29,Basant Mahapatra,Engineering,600000
32,Ajay Patel,Testing,350000
34,Swaraj Birla,Testing,350000
Each line consists of 4 fields
"Employee ID," "Name," "Department," and "Salary."
Here, "Employee ID" and "Salary" are integers, while "Name" and "Department" are strings that do not contain commas or newlines.
Currently, below program reads the input and creates an array of String from the lines of input. Then it calls a method processData on with this array, and prints the returned data to the output file.
Unfortunately, processData currently does not do anything useful - it just returns an empty Dictionary<String,int>.
I have to modify processData find the Employee IDs of the highest paid employee in each department. Specifically, processData should return a dictionary where each key is the name of a department,and the value is the Employee ID of the employee in that department who has the highest salary.
In case multiple employees in a department are tied for the highest salary, you can pick any one of them.
Engineering: 22
Testing: 23
Sample program
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ProgramNamespace
{
public class Program
{
public static Dictionary<String, int> processData(IEnumerable<string> lines)
{
Dictionary<String, int> retVal = new Dictionary<String, int>();
return retVal;
}
static void Main(string[] args)
{
try
{
Dictionary<String, int> retVal = processData(File.ReadAllLines("input.txt"));
// code to write in output file
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
I have an idea:
create an Class name "EmployeeSalary"(or other name you like)
parse all lines into List< EmployeeSalary > (use split, and create new object)
use linq to query List< EmployeeSalary >(group by department and find max salary)
generate query result to dictionary you want.
If you are familiar with linq, that would be much easier to get the idea.
public Dictionary<String, int> processData(IEnumerable<string> lines)
{
List<EmployeeSalary> list = new List<EmployeeSalary>();
foreach(var line in lines)
{
string[] temp = line.Split(',');
EmployeeSalary tempObj = new EmployeeSalary();
tempObj.EmployeeID = int.Parse(temp[0]);
tempObj.Name = temp[1];
tempObj.Department = temp[2];
tempObj.Salary = int.Parse(temp[3]);
list.Add(tempObj);
}
var query = (from f in list group f by f.Department into g select g);
Dictionary<String, int> retVal =
query.ToDictionary(x => x.Key, x => x.OrderByDescending(c=>c.Salary).Select(c=>c.EmployeeID).FirstOrDefault());
return retVal;
}
You could do it as following if you want to do it with Dictionary<string,int>. the Crux of idea is to create an intermediate list comprising of all Employees and then Group them by Department, before Ordering By Salary(desc) and taking the first one.
public static Dictionary<String, int> processData(IEnumerable<string> lines)
{
Dictionary<String, int> retVal = new Dictionary<String, int>();
var list = new List<Employee>();
foreach(var user in lines)
{
var userDetails = user.Split(new []{','},StringSplitOptions.RemoveEmptyEntries);
list.Add(new Employee
{
Id=int.Parse(userDetails[0]),
UserName = userDetails[1],
Department = userDetails[2],
Salary = int.Parse(userDetails[3])
});
}
retVal = list.GroupBy(x=>x.Department).Select(x=>x.ToList()
.OrderByDescending(c=>c.Salary).First())
.ToDictionary(x=> x.Department, y=> y.Id);
return retVal;
}
Where Employee is defined as
public class Employee
{
public int Id;
public string UserName;
public int Salary;
public string Department;
}
Output

MongoDB C# Driver - Fastest way to perform an "IN" query on _id

I'm trying to get values from a collection, based on items whose IDs are in a certain collection of IDs.
My current code to build the filter is:
IEnumerable<string> IDList;
using (var enumerator = IDList.GetEnumerator())
{
if (enumerator.MoveNext() == false) return null; // empty collection
// take the first key
var key = enumerator.Current;
filter = Builders<MyClass>.Filter.Eq(p => p.Key, key);
// take all the other keys
while (enumerator.MoveNext())
{
var innerKey = enumerator.Current;
filter = filter | Builders<MyClass>.Filter.Eq(p => p.Key, innerKey);
}
}
and then my code to get the items is:
List<MyClass> values = new List<MyClass>();
using (var cursor = await MyCollection.FindAsync(filter))
{
while (await cursor.MoveNextAsync())
{
values.AddRange(cursor.Current);
}
}
This code's performance seems pretty subpar, and I'm sure there has to be a faster way since MongoDB should have very good performance... Not to mention I'm querying an indexed field, which should make the query very fast. What can I do to speed this up, both in an async way and a sync way? From some Googling I've seen that there are many ways to query a collection, and I'm not sure which way would be the best for my particular case.
Running this query in RoboMongo takes 0.02 seconds, while running it in C# MongoDb.Driver takes a full second, sometimes even longer and I'm not sure why.
Thanks in advance.
How about a simple "$in" query?
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class MyClass
{
public ObjectId Id;
public string Key;
}
public class Program
{
static void Main(string[] args)
{
IEnumerable<string> ids = new [] { "a", "b", "c" };
var collection = new MongoClient().GetDatabase("test").GetCollection<MyClass>("test");
foreach (var id in ids)
{
collection.InsertOne(new MyClass { Key = id });
}
// here comes the "$in" query
var filter = Builders<MyClass>.Filter.In(myClass => myClass.Key, ids);
// sync
List<MyClass> values = collection.Find(filter).ToList();
// async
var queryTask = collection.FindAsync(filter);
values = GetValues(queryTask).Result;
Console.ReadLine();
}
private static async Task<List<MyClass>> GetValues(System.Threading.Tasks.Task<IAsyncCursor<MyClass>> queryTask)
{
var cursor = await queryTask;
return await cursor.ToListAsync<MyClass>();
}
}
}

iterate a number of dictionary items

I have the following Dictionary:
Dictionary<string, List<string>> myList = new Dictionary<string,List<string>>();
This dictionary gets populated, I'm never going to know how many items I will have.
An example of the output:
Elements of Dictionary: (Key,Value)
{"Code",{"Test1", "Test2", "Test3"}}
{"Desc",{"Desc1", "Desc2", "Desc3"}}
{"Name",{"name1", "name2", "name3"}}
How can I loop through every dictionary and get the value i.e. by index
something that would yield ->
{Code = "Test1", Desc = "desc", Name = "name1"}
{Code = "Test2", Desc = "desc2", Name = "name2"}
{Code = "Test3", Desc = "desc3", Name = "name3"}
Any ideas?
Thanks
The key is to build a set of arrays for each column of your data instead of for each rows like the Dictionary enumerator will provide you. This can be achieved through the use of the Aggregate extension method and a simple Select statement.
// Assuming the following class as a destination type
class Foo
{
public Foo(string[] values)
{
Code = values[0];
Name = values[1];
Desc = values[2];
}
public string Code;
public string Name;
public string Desc;
}
// This would be the code required to parse the data
var destination = dataSource["Code"].Aggregate(new List<Foo>(), (entries, _) =>
{
var curentRow = entries.Count();
var entryData = dataSource.Select(property => property.Value[curentRow]).ToArray();
entries.Add(new Foo(entryData));
return entries;
});
In this case, we use the Code property as a key to figure out how many entries there are in your data source (your dictionary). If there are rows in your dictionary with missing values (less items than in the Code row), this code will fail as it assumes that there are the same amount of items in all the rows.
The Aggregate method acts like a for loop in this case, providing us with a basic counter named currentRow that we will use later to access specific entries in your data. This counter is the amount of entries we stored into the List<Foo>. It starts at 0 and it increments each time we add a new value to the result set.
The next step is to look at all the entries in your data source and to access the value that matches the current row. We then convert it into an array and feeds it into the constructor of your destination type that knows how to deserialize this data.
This is a basic solution which perfectly solves your problem. I have created an IEnumerable object so that you can increment foreach loop by "Code","Desc",... .
Once you get the strings( which are Keys of Dictionary ) you can interate through the list that is the value of each key in the dictionary. The code below Writes and counts all elements in Dictionary and for each Values writes all elements in Lists.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
public static IEnumerable<string> Helper() // Neccessary to use in foreach loop
{
yield return "Code";
yield return "Desc";
yield break;
}
static void Main(string[] args)
{
Dictionary<string, List<string>> container = new Dictionary<string, List<string>>();
List<string> l1 = new List<string>();
List<string> l2 = new List<string>();
l1.Add("l1s1");
l1.Add("l1s2");
l1.Add("l1s3");
l2.Add("l2s1");
l2.Add("l2s2");
l2.Add("l2s3");
container.Add("Code", l1);
container.Add("Desc", l2);
int count = 0;
foreach (string k in Helper()) // get all Keys
{
for (int i = 0; i < container[k].Count; i++)
{
Console.WriteLine(container[k][i].ToString()); // Write each element in list
count++;
}
Console.WriteLine();
}
Console.WriteLine(count.ToString());
}
}
}
Note that you don't need to know how many elements will be in each list.Output is like :
Or without creating Helper() you can do like below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, List<string>> container = new Dictionary<string, List<string>>();
List<string> l1 = new List<string>();
List<string> l2 = new List<string>();
l1.Add("l1s1");
l1.Add("l1s2");
l1.Add("l1s3");
l2.Add("l2s1");
l2.Add("l2s2");
l2.Add("l2s3");
container.Add("Code", l1);
container.Add("Desc", l2);
int count = 0;
foreach (string k in new string [] {"Code","Desc"}) // get all Keys
{
for (int i = 0; i < container[k].Count; i++)
{
Console.WriteLine(container[k][i].ToString()); // Write each element in list
count++;
}
Console.WriteLine();
}
Console.WriteLine(count.ToString());
}
}
}
looks like the wrong data structure. Seems like you have a set of objects each with an arbitrary set of attributes. You need map<string,map<string,string>>
"item1"->code:'code1",desc:"desc1"....
"item2"->code:'code2",desc:"desc2", color:"red", size:'42"
......

Find matching KVP from Dictionary<List<enum>,string> where search key is List<enum> and return reverse partial matches

I have a Dictionary where the key is a list of enum values, and the value is a simple string.
What I need to do is using another list of enum values find the match KVP.
The curveball and reason for posting here is I also need it to return KVP if the list from my test or search list contains all the items (or enum objects) in any key in the dictionary.
example excerpt of code:
public enum fruit{ apple , orange , banana , grapes };
public class MyClass
{
public Dictionary<List<fruit>, string> FruitBaskets = new Dictionary<List<fruit>, string>;
FruitBaskets.Add(new List<fruit>{apple,orange},"Basket 1");
List<fruit> SearchList = new List<fruit>{orange,apple,grapes};
}
I need to search the dictionary for SearchList and return "Basket 1".
Note that the matching may be backwards than what you would expect for such an example as I need the key to match agains the search list and not vice versa, so extra items in the search list that are not in the key are ok.
I know I could simply iterate the dict and check one by one but I also need this to be as fast as possible as it resides in a loop that is running fairly fast.
What I am currently using is;
public Dictionary<List<fruit>, string> SearchResults;
foreach (KeyValuePair<List<fruit>, string> FruitBasket in FruitBaskets)
{
if (FruitBasket.Key.Except(SearchList).Count() == 0)
SearchResults.Add(FruitBasket);
}
Wondering if there is a better/faster way.
You need to rethink about you choice of Keys in dictionary. There are some major problem with List keys, such as:
You can't use O(1) key lookup with List
Your keys aren't immutable
You can have identical lists as keys without receiving errors, for example you can have:
var a = new[] { fruit.organge }.ToList();
var b = new[] { fruit.organge }.ToList();
fruitBasket.Add(a, "1");
fruitBasket.Add(b, "2");
But is this dictionary valid? I guess not but it depends on your requirements.
You can change Dictionary keys!
For this reasons, you need to change your dictionary key type. You can use combined Enum values instead of using a List with bitwise operators. For this to work, you need to assign powers of 2 to each enum value:
[Flags]
public Enum Fruit
{
Orange = 1,
Apple = 2,
Banana = 4,
Grape = 8
}
You have to combine these enum values to get the desired multi-value enum dictionary key effect:
For [Fruit.Orange, Fruit.Apple] you use Fruit.Orange | Fruit.Apple.
Here's a sample code for combining and decomposing values:
private static fruit GetKey(IEnumerable<fruit> fruits)
{
return fruits.Aggregate((x, y) => x |= y);
}
private static IEnumerable<fruit> GetFruits(fruit combo)
{
return Enum.GetValues(typeof(fruit)).Cast<int>().Where(x => ((int)combo & x) > 0).Cast<fruit>();
}
Now you need a function to get all combinaions (power set) of the SearchList:
private static IEnumerable<fruit> GetCombinations(IEnumerable<fruit> fruits)
{
return Enumerable.Range(0, 1 << fruits.Count())
.Select(mask => fruits.Where((x, i) => (mask & (1 << i)) > 0))
.Where(x=>x.Any())
.Select(x=> GetKey(x));
}
Using these combinations, you can lookup values from dictionary using O(1) time.
var fruitBaskets = new Dictionary<fruit, string>();
fruitBaskets.Add(GetKey(new List<fruit> { fruit.apple, fruit.orange }), "Basket 1");
List<fruit> SearchList = new List<fruit> { fruit.orange, fruit.apple, fruit.grapes };
foreach (var f in GetCombinations(SearchList))
{
if (fruitBaskets.ContainsKey(f))
Console.WriteLine(fruitBaskets[f]);
}
Consider storing your data in a different way:
var FruitBaskets = Dictionary<fruit, List<string>>();
Each entry contains elements that match at least one fruit. Conversion from your structure is as follows:
foreach (var kvp in WobblesFruitBaskets)
{
foreach (var f in kvp.Key)
{
List<string> value;
if (!FruitBaskets.TryGetValue(f, out value))
{
value = new List<string>();
FruitBaskets.Add(f, value);
}
value.Add(kvp.Value);
}
}
Now, the search would look like this: For a composed key searchList you first calculate results for single keys:
var partialResults = new Dictionary<fruit, List<string>>();
foreach (var key in searchList)
{
List<string> r;
if (FruitBaskets.TryGetValue(key, out r))
{
partialResults.Add(key, r);
}
}
Now, what is left is to compose all possible search results. This is the hardest part, which I believe is inherent to your approach: for a key with n elements you have 2n - 1 possible subkeys. You can use one of subset generating approaches from answers to this question and generate your final result:
var finalResults = new Dictionary<List<fruit>, List<string>>();
foreach (var subkey in GetAllSubsetsOf(searchList))
{
if (!subkey.Any())
{
continue; //I assume you don't want results for an empty key (hence "-1" above)
}
var conjunction = new HashSet<string>(partialResults[subkey.First()]);
foreach (var e in subkey.Skip(1))
{
conjunction.IntersectWith(partialResults[e]);
}
finalResults.Add(subkey, conjunction.ToList());
}
I've changed string to List<string> in result's value part. If there is some invariant in your approach that guarantees there will be always only one result, then it should be easy to fix that.
if you create a Dictionary from a Reference Type, you stored just the Reference (Not value), then you can't use simply FruitBaskets[XXX] (except you use the same key that you create the node of dictionary), you must iterate whole of Keys in your dictionary.
I think this function is easy and good for you:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
if (Enumerable.SequenceEqual<fruit>(KEY,item))
return true;
}
return false;
}
and this,
bool B = Contain(new List<fruit> { fruit.apple, fruit.orange }); //this is True
But if you want to consider the permutation of members, you can use this function:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
HashSet<fruit> Hkey= new HashSet<fruit>(KEY);
if (Hkey.SetEquals(item))
return true;
}
return false;
}
and here's the output:
bool B1 = Contain(new List<fruit> { fruit.orange, fruit.grapes }); // = False
bool B2 = Contain(new List<fruit> { fruit.orange, fruit.apple }); // = True
bool B3 = Contain(new List<fruit> { fruit.apple, fruit.orange }); // = True

Categories