I have a situation, where I get data from the database in such a way, that everything is stored in one-dimensional array.
For example:
User table: UserId, Name
Group table: GroupId, Name
UserGroup table: UserId, GroupId
As a result of joining these tables I obtain array of the following form:
result[0] = "1" // user id
result[1] = "John Doe" // user name
result[2] = "121" // group id
result[3] = "SomeGroup" // group name
result[4] = "1" // user id
result[5] = "John Doe" // user name
result[6] = "2135" // group id
result[7] = "SomeOtherGroup" // group name
I know it's not a good solution of keeping data, but these data are coming to me from some other piece of code which I am not allowed to change, so I have to deal with it.
My questions are:
Pure linq Expression :
int i = 0;
var objects = result.GroupBy(x => Math.Floor(i++ / 4.0))
.Select(g => new { id =g.ElementAt(0), name = g.ElementAt(1), gId= g.ElementAt(2), group = g.ElementAt(3)})
.GroupBy(x=>new {x.id, x.name}, x=>new {x.gId, x.group})
.Select(y=>new {y.Key, groups = y.ToList()});
GroupBy
I group results in 4 elements subsets using a floor and a temporary variable. Select
put the resulting arrays in an anonymous type for better usability in the next steps. GroupBy
is used to group the entries by Employee. The Key will be the employee and the values will be the corresponding Groups. Select
is used to put the GroupBy
result in a better shape. I choose to put the result in an other anonymous type but You could instantiate you custom objects here and put the values in the right fields using curly brace constructor. If your logic depends on indexes LINQ is is rarely the right tool. It results in less readable, maintainable, efficient and robust code than with plain loops.
I would use something like following to create two dictionaries representing the many to many relation. Note the for
-loop which increments by 4 on every iteration since that seems to be the user-group-"package":
var userIdGroups = new Dictionary<int, HashSet<Group>>();
var groupIdUsers = new Dictionary<int, HashSet<User>>();
for(int i = 0; i < result.Length; i += 4)
{
int id;
if(int.TryParse(result[i], out id))
{
string name = result.ElementAtOrDefault(i + 1);
if(name == null)
continue; // end, invalid data
User user = new User{ UserId = id, Name = name };
string groupID = result.ElementAtOrDefault(i + 2);
if(!int.TryParse(groupID, out id))
continue; // end, invalid data
name = result.ElementAtOrDefault(i + 3);
if(name == null)
continue; // end, invalid data
Group group = new Group{ GroupId = id, Name = name };
HashSet<Group> userGroups;
HashSet<User> groupUsers;
if (userIdGroups.TryGetValue(user.UserId, out userGroups))
userGroups.Add(group);
else
userIdGroups.Add(user.UserId, new HashSet<Group>{ group });
if (groupIdUsers.TryGetValue(group.GroupId, out groupUsers))
groupUsers.Add(user);
else
groupIdUsers.Add(group.GroupId, new HashSet<User> { user });
}
}
The result is:
You have to override Equals
and GetHashCode
to compare the ID's:
class User
{
public int UserId { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
User u2 = obj as User;
if (u2 == null) return false;
return UserId == u2.UserId;
}
public override int GetHashCode()
{
return UserId;
}
}
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
Group g2 = obj as Group;
if (g2 == null) return false;
return GroupId == g2.GroupId;
}
public override int GetHashCode()
{
return GroupId;
}
}
You can do it with the basic structures like loops:
void Main()
{
var result = new string[] {"1","John Doe","2","Joes Group","3","Jack Daniel","4","Jacks Group","5","Foo Bar","6","FooBar Group",};
List<Person> personList = new List<Person>();
List<Group> groupList = new List<Group>();
for(int i = 0; i < result.Length; i+=2)
{
i = i + 2;
//check if group does not already exist
groupList.Add(new Group() {Name = result[i+1]});
}
for(int i = 0; i < result.Length; i+=2)
{
//check if person exists.
//if person only add group to person personList.Where(x => x.Name ==result[i+1])....
personList.Add(new Person() { Id = int.Parse(result[i]),
Name = result[i+1],
Groups = new List<Group>()
{
groupList.FirstOrDefault (l => l.Name == result[i+3])
}
});
i = i+2;
}
personList.Dump();
}
public class Person
{
public Person()
{
Groups = new List<Group>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<Group> Groups { get; set; }
}
public class Group
{
public string Name { get; set; }
}
// Define other methods and classes here
Output:
Please take advise: this code does not contain any validation logic, or duplicate checks. You'll have to imlpement this by yourself. But before you implement something like this, I'd rather change the way you get your data delivered. this way you would deal with the root of your peroblems not with the symptoms.
i think no need to linq
//some class
public class Result
{
public string UserId {get;set;}
public string UserName {get;set;}
public string GroupId {get;set;}
public string GroupName {get;set;}
public string UserGroupUserId {get;set;}
public string UserGroupUserName {get;set;}
public string UserGroupId {get;set;}
public string UserGroupGroupId {get;set;}
}
// your array
private void Form1_Load(object sender, EventArgs e)
{
string[] result = new string[8];
result[0] = "1";
result[1] = "John Doe";
result[2] = "121";
result[3] = "SomeGroup";
result[4] = "1";
result[5] = "John Doe";
result[6] = "2135";
result[7] = "SomeOtherGroup";
Result r = CastResult(result);
}
// simple cast array to some type
public Result CastResult(string[] array)
{
return new Result() { UserId=array[0], UserName=array[1], GroupId=array[2], GroupName=array[3], UserGroupUserId=array[4], UserGroupUserName=array[5] , UserGroupId=array[6], UserGroupGroupId=array[7] };
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.