private RepoTreeItem FilterByTagPrefix(ILoadedRepository repo, string prefix, Func <string, string> convertor) { // var nodes = new ObservableCollection<PackageCategoryTreeItem>(); var map = new Dictionary <string, List <PackageMenuItem> >(); foreach (var package in repo.Packages.Packages) { if (package.Value == null) { continue; } IDescriptor descriptor = package.Value !; var pkgKey = PackageKey.Create(repo.Index.Url, descriptor.Id); var release = repo.Release(pkgKey); var payload = release?.WindowsExecutable(); if (release == null || payload == null) { continue; } var tags = Iter.ToArray(descriptor.Tags.Where(x => x.StartsWith(prefix))); if (tags.IsNullOrEmpty()) { tags = new[] { prefix }; } foreach (var tag in tags) { if (!map.ContainsKey(tag)) { map[tag] = new List <PackageMenuItem>(); } map[tag].Add(new PackageMenuItem(_store, pkgKey, payload, descriptor.NativeName() ?? "<>", release.Version)); } } var categoriesSet = new SortedSet <PackageCategoryTreeItem>(map.OrderBy(x => x.Key).Map(x => { x.Value.Sort(); var items = new ObservableCollection <PackageMenuItem>(x.Value); return(new PackageCategoryTreeItem(_store, convertor(x.Key), items)); })); var categories = new ObservableCollection <PackageCategoryTreeItem>(categoriesSet); return(new RepoTreeItem(repo.Index.NativeName(), categories)); }