I am using GroupBy create a hierarchical set of groups to use in multiple child grids.
products.json
[
{
"group": "coloris", // LEVEL 1
"valeursAndUids": [
{
"Item1": "Beige",
"Item2": "QB32-20220325-486274"
},
{
"Item1": "Beige",
"Item2": "QB32-20220325-106045"
},
{
"Item1": "Venezia",
"Item2": "QB32-20220325-205994"
},
{
"Item1": "Venezia",
"Item2": "QB32-20220325-270903"
}
]
},
{
"group": "ref_commercial", // LEVEL 2
"valeursAndUids": [
{
"Item1": "29245",
"Item2": "QB32-20220325-486274"
},
{
"Item1": "29245",
"Item2": "QB32-20220325-106045"
},
{
"Item1": "29245",
"Item2": "QB32-20220325-205994"
},
{
"Item1": "29245",
"Item2": "QB32-20220325-270903"
}
]
},
{
"group": "Address", // LEVEL 3
"valeursAndUids": [
{
"Item1": "172 B",
"Item2": "QB32-20220325-486274"
},
{
"Item1": "172 B",
"Item2": "QB32-20220325-106045"
},
{
"Item1": "1725 A",
"Item2": "QB32-20220325-205994"
},
{
"Item1": "1725 A",
"Item2": "QB32-20220325-270903"
}
]
}
//....
]
How do I get a grouping of products that are grouped by group value
?
Basically I want it to output like so:
coloris Beige // Level 1
ref_commercial 29245 // Level 2
Address 172B // Level 3
QB32-20220325-486274
QB32-20220325-106045
...
coloris Venezia
ref_commercial 29245
Address 1725 A
QB32-20220325-205994
QB32-20220325-270903
...
coloris N
ref_commercial N
Address N
// Level N
...
This way I regrouped all the values by group/sub groups by respecting the level ( the group is already sorted so) I can have many levels, but always I want to depend on the group value. Is there a way to do it?
CodePudding user response:
I believe you want to get the key and group name of the group, the iterate over the subgroups. Something like this should work:
public void GenerateGroups(List<Product> myList)
{
var groups = myList.GroupBy(g => g.Group);
foreach (var group in groups)
{
var groupName = group.Key;
var groupValue = group.First().Group;
foreach (var group2 in group)
{
}
}
}
I setup a simple couple of classes to illustrate:
internal class Product
{
public string? Group { get; set; }
public List<ValeursAndUids>? ValeursAndUids { get; set; }
}
internal class ValeursAndUids
{
public string? Item1 { get; set; }
public string? Item2 { get; set; }
}
CodePudding user response:
I have not come up with anything better than building a tree and printing it using the visitor pattern.
First of all, we need to build a flat list of products:
// just collect all groups into a single list
// it will produce the list: ["coloris", "ref_commercial", "Address", ""]
List<string> groups = listProducts.Select(p => p.Group).ToList();
groups.Add(""); // group for uids
// build flat list of products
// each product will be represented as a list of group values:
// [ ["Beige", "29245", "172 B", "QB32-20220325-486274"], ["Beige", "29245", "172 B", "QB32-20220325-106045"],... ]
IEnumerable<List<string>> products = listProducts
.SelectMany(product => product.ValeursAndUids, (product, valueAndUid) => new { product.Group, valueAndUid })
.GroupBy(item => item.valueAndUid.Item2)
.Select(g =>
{
List<string> path = g.Select(item => item.valueAndUid.Item1).ToList();
path.Add(g.Key);
return path;
});
Now we can build a tree:
TreeNode root = new TreeNode("root", "");
TreeNode parent = root;
foreach (List<string> path in products)
{
TreeNode current = parent;
for (int i = 0; i < path.Count; i )
{
string part = path[i];
parent = parent.GetChild(groups[i], part);
}
parent = current;
}
and print it:
root.Accept(new DepthTreeVisitor());
Output:
root
coloris Beige
ref_commercial 29245
Address 172 B
QB32-20220325-486274
QB32-20220325-106045
coloris Venezia
ref_commercial 29245
Address 1725 A
QB32-20220325-205994
QB32-20220325-270903
TreeNode
and Visitor
definitions:
public class TreeNode {
public TreeNode(string groupName, string value)
{
GroupName = groupName;
Value = value;
Children = new HashSet<TreeNode>();
}
public string Value { get; }
public string GroupName { get; }
public ISet<TreeNode> Children { get; }
public void Accept(Visitor visitor)
{
visitor.VisitTree(this);
}
public TreeNode GetChild(string group, string value)
{
foreach (TreeNode child in Children)
{
if (child.Value.Equals(value))
{
return child;
}
}
return GetChild(new TreeNode(group, value));
}
private TreeNode GetChild(TreeNode child)
{
Children.Add(child);
return child;
}
}
public interface Visitor
{
public void VisitTree(TreeNode tree);
}
public class DepthTreeVisitor : Visitor
{
private int level = 0;
public void VisitTree(TreeNode tree)
{
string indentString = new string(' ', 2 * level);
Console.WriteLine($"{indentString}{tree.GroupName} {tree.Value}");
level ;
foreach (TreeNode child in tree.Children)
{
child.Accept(this);
}
level--;
}
}
Here is complete demo.