0

I have entity AdminResource. Table has columns:

id | resource | path | parent | slug
1    Sport      1       0       sport
2    Football   1-2     1       sport-football
3    Estonia    1-2-3   2       sport-football-estonia

In my controller I get data

List<AdminResource> resources = resourceDAO.findAdminResources(user_id);

But I have problem now. I want to make new formated array/object with children items. Like this (by PHP, Javascript experience):

0: {
  id: 1,
  resource: Sport,
  children: {
       0: {
         id: 2,
         resource: Football,
         children: {
          id:....
         }
       } 
 }
}

Children can be very deep. How I can create multidimensional array? I this know in PHP and in Nodejs.

But in Java I have had a lot of errors. Yes, I know about recursive logic. But... I can't create with ArrayList, because I got error - key must be int. I don't understand about HasMap, how I can create deep list.

I can't find similar examples in Google, maybe I can't understand its. I can't understand how need work with multidimensional arrays/object in Java.

Audr
  • 69
  • 2
  • 3
  • 9
  • Read about the [Composite Pattern](https://en.wikipedia.org/wiki/Composite_pattern) which describes how to create recursive object structures. For this issue, you should write a class `AdminNode` that has three properties: `id`, `resource`, and `children`, which is a list of `AdminNode`s again. – Malte Hartwig Dec 19 '17 at 11:00

2 Answers2

0

From what I can gather from the data you provided, I think what you want isn't a multidimensional array.

I think a tree or maybe an oriented graph if there can be multiple parents (same as the tree except a Node would have an array of parent nodes) is what you want.

finrod
  • 72
  • 1
  • 9
  • Thanks @finrod. But I can't uderstand, how I can iterate Tree in my Controller, where I have `List resources = resourceDAO.findAdminResources(user_id);` – Audr Dec 19 '17 at 13:56
  • In a certain way you already have a tree in that List, you just have to treat it like one. Assuming the first element in that list is the root (the first parent) you can search the List for other elements that have the root as a parent. Say you find elements A and B as children of the root. You can then search the List again for elements that have A as a parent or B as a parent and so on and so on until you can't find any more children. This way you can construct the hierarchy you posted. – finrod Dec 20 '17 at 14:46
  • You can check the different Tree Traversal algorithms here: http://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/ – finrod Dec 20 '17 at 14:48
0

As finrod already explained, arrays are not what you are looking for. Judging by the {} syntax in your example, it looks more like a hierarchy of objects.

A tree seems to be a good option. As I said in my comment, a tree consists of nodes that hold a value (AdminResource in this case) and have children (a list of other nodes).

Here is a very basic example:

public static void main(String[] args)
{
    List<AdminResource> resources = Arrays.asList(new AdminResource("Sport", Arrays.asList(1)),
                                                  new AdminResource("Football", Arrays.asList(1, 2)),
                                                  new AdminResource("Estonia", Arrays.asList(1, 2, 3)));

    AdminNode root = new AdminNode(new AdminResource("ROOT", Collections.emptyList()));

    resources.forEach(root::addResource);

    for (AdminResource r : root)
    {
        System.out.println(r.getId());
    }
}

public static class AdminNode
    implements Iterable<AdminResource>
{
    private AdminResource   resource;
    private List<AdminNode> children;

    public AdminNode(AdminResource resource)
    {
        this.resource = resource;
        this.children = new ArrayList<>();
    }

    public void addResource(AdminResource resource)
    {
        addResource(resource, new LinkedList<>(resource.getPath()));
    }

    private void addResource(AdminResource resource, Queue<Integer> path)
    {
        if (path.size() > 1)
        {
            Integer nextParent = path.poll();
            for (AdminNode child : children)
            {
                if (child.getResource().getId().equals(nextParent))
                {
                    child.addResource(resource, path);
                }
            }
        }
        else
        {
            children.add(new AdminNode(resource));
        }
    }

    public AdminResource getResource() { return resource; }

    @Override
    public Iterator<AdminResource> iterator()
    {
        return stream().iterator();
    }

    public Stream<AdminResource> stream()
    {
        return goDown(this).skip(1).map(AdminNode::getResource);
    }

    private static Stream<AdminNode> goDown(AdminNode node)
    {
        return Stream.concat(Stream.of(node), node.children.stream().flatMap(AdminNode::goDown));
    }
}

public static class AdminResource
{
    private Integer       id;
    private String        resource;
    private List<Integer> path;

    public AdminResource(String resource, List<Integer> path)
    {
        this.id = path.isEmpty() ? null : path.get(path.size() - 1);
        this.resource = resource;
        this.path = path;
    }

    public Integer getId() { return id; }
    public List<Integer> getPath() { return path; }
}

The important class is AdminNode. You start with a dummy root node, which offers a method to add more AdminResources. That method recursively crawls down the path of the new resource, and finds the right place to add it. Similar methods can be written for removal or searching.

As I said, this a very basic example. It assumes that your list of resources is properly order. It ignores resources if the path to them is not existent yet. And so on...

But this should give you an idea of what trees are and how to start. They are used in a lot of places. A common usage is the component hierarchy of a User Interface, for example.

Malte Hartwig
  • 4,477
  • 2
  • 14
  • 30
  • I can't create static class, because it is in package. Does it is important? – Audr Dec 20 '17 at 11:05
  • The `static` is only relevant when it is an inner class. If you have it in a separate java file, you can remove the `static`. – Malte Hartwig Dec 20 '17 at 12:59
  • Thanks, I understood. But now I have new problem. I can't iterate tree results. `resources.forEach(root::addResource); root.getResource.forEach` or `for (Object i: root)` doesn't work. Error: can olny iterate over array... How I can get array? – Audr Dec 20 '17 at 13:56
  • @Audr as I said, this was a basic example. You really should check out how trees work. I have made the node implement `Iterable` and a `stream()` method to show how to access the nodes. It iteratively goes down the tree, level by level. As mentioned before, all of those methods from the `Collection` class will be implemented in a similar way, but adding them is not the scope of this question. If you need more functionality, read about trees and ask another question. – Malte Hartwig Dec 20 '17 at 14:21
  • I have been working 2 days, but I can't get from `for (AdminResource r : root)` r.children. I get Sport and Football in one level, but I want to ger in the first level Sport with it children. Where one child will be for ex. Football. – Audr Dec 22 '17 at 13:12
  • Not sure I completely understand. The `iterator` method I added iterates all nodes in one go. If you want to go level by level, node by node, use `for (AdminResource childOnFirstLevel : root.getChildren())`. – Malte Hartwig Dec 22 '17 at 21:59