Собрать дерево из коллекций

Как можно получить дерево из следующего

Структура папок

name       |  folderId     |  parentId
test 1     |  1            |  2
test 2     |  2            |  0 //значит это корневой уровень
name 3     |  3            |  0 //значит это корневой уровень 
name 4     |  4            |  1
name 5     |  5            |  1

Структура объектов

name         |  folderId   
object 1     |  1         
object 2     |  2       
object 3     |  3    
object 4     |  3         
object 5     |  3     

 

Я получаю

val folders = listOf(table1)
val objects = listOf(table2)  

Из этого нужно получить List<TreeItem>

test 2 -> {
    test 1 -> {     
        name 4
        name 5
    }
    object 1
}

name 3 -> {
    object 3
    object 4
    object 5
}

Модели данных

sealed class TreeItem {
    class Folder(val folderInfo: TreeFolder, val childs: List<TreeItem>) : TreeItem()
    class Object(val item: Object) : TreeItem()
}

data class TreeFolder(val name: String, folderId: Long, parentId: Long)
data class Object(val name: String, folderId: Long)

Ответы (2 шт):

Автор решения: SKULL

Вот так я собрал дерево

fun getTreeObj(): Observable<List<TreeItem>> {
    return Observables.zip(
        treeFolderRepository.getTree(),
        objectRepository.getObjects()
            .toObservable()
            .distinctUntilChanged()
    ) { folders, objects -> Pair(folders, objects) }
        .observeOn(Schedulers.computation())
        .map { treeItems ->
            val objects = treeItems.second.toMutableList()
            val folders = treeItems.first
            
            if (folders.isEmpty()) {
                return@map objects.map { TreeItem.Object(item = it) }
            }

            val treeFolderMap: MutableMap<Long, TreeItem.Folder> =
                folders.map { TreeItem.Folder(folderInfo = it) }
                    .associateBy { it.folderInfo.folderId }
                    .toMutableMap()

            treeFolderMap.forEach { folder ->
                folder.value.childs.addAll(
                    objects.filter { it.first.folderId == folder.value.folderInfo.folderId }.let {
                        objects.removeAll(it)
                        it.map { TreeItem.Object(item = it) }
                    }
                )
            }

            val iterator = treeFolderMap.iterator()
            while (iterator.hasNext()){
                iterator.next().apply {
                    treeFolderMap[value.folderInfo.parentId]?.let { parentFolder ->
                        parentFolder.childs.add(value)
                        iterator.remove()
                    }
                }
            }
            return@map treeFolderMap.values.toList() as List<TreeItem>
        }
        .observeOn(AndroidSchedulers.mainThread())
}
→ Ссылка
Автор решения: Aleksey

Создаёшь структуру HashMap<String, TreeItem> где ключ это id папки и значение это объект папки

Далее добавляешь туда все папки После проходишь циклом каждый объект:

  1. Получаешь ID родительской папки
  2. Находишь папку по ID в HashMap
  3. Присоединяешь туда текущий элемент как дочерний Дальше проходишь по объектам(objects) и так-же цепляешь к нужным родителям.

В List<TreeItem> я так понял должны содержатся элементы которые находятся в корне, можешь объявить этот объект с самого начала и если у элемента нет родителя то добавляешь его сюда.

Должно получиться как-то так

public List<TreeItem> func(List<TreeItemInfo> folders, List<TreeItemInfo> objects) {

    HashMap<String, TreeItem> foldersAndObjects =  new HashMap<>();
    List<TreeItem> root = new ArrayList<>();

    // Ложим папки в hashMap по ID
    for (TreeItemInfo folder : folders) {
        foldersAndObjects.put(folder.getId(), new TreeItem(folder));
    }

    // Устанавливаем связи родитель-ребенок для папок
    for (TreeItemInfo folder : folders) {
        String parentID = folder.parentID(); // Получаем ID родителя
        if (parentID == null) {
            // Если родителя нет ложем узел в корень
            root.add(new TreeItem(folder));
        } else {
            // Если ключ родителя есть, ищем его и присоединяем текущее значение как дочерний элемент
            foldersAndObjects.get(parentID).setChild(new TreeItem(folder));
        }
    }

    // Добавляем объекты в папки или в root
    for (TreeItemInfo object : objects) {
        String parentID = object.parentID();
        if (parentID == null) {
            root.add(new TreeItem(object));
        } else {
            foldersAndObjects.get(parentID).setChild(new TreeItem(object));
        }
    }

    // Возвращаем список корневых элементов
    return root;
}

TreeItem - это должна быть структура у которой есть значения: значение узла, ссылки на родителя и дочерние узлы

TreeItemInfo - Это грубо говоря объект содержаний информацию по которой можно получить ID родителя и по его значению можно создать узел TreeItem

→ Ссылка