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?