Change array structure [closed]

-2

How to change the structure of an array? It can be using excel or via programming. I already did my solution with Java but it was a damn gambiarra :). Plus it's not generic enough.

Is there a design pattern that solves this? The final goal is to be able to sort the table by date, which is not possible with the table in the initial format.

    
asked by anonymous 28.08.2017 / 20:13

1 answer

1

Let's first create a class Tabela :

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;

class Tabela<L, C, X> {
    private final Set<C> colunas;
    private final Set<L> linhas;
    private final Map<L, Map<C, X>> celulas;
    private final Function<? super L, String> strLinhas;
    private final Function<? super C, String> strColunas;
    private final Function<? super X, String> strCelulas;

    @FunctionalInterface
    public static interface TabelaConsumer<L, C, X> {
        public void accept(L linha, C coluna, X celula);
    }

    public Tabela(
            Collection<L> linhas,
            Collection<C> colunas,
            Function<? super L, String> strLinhas,
            Function<? super C, String> strColunas,
            Function<? super X, String> strCelulas)
    {
        this.colunas = new LinkedHashSet<>(colunas);
        this.linhas = new LinkedHashSet<>(linhas);
        this.strLinhas = strLinhas;
        this.strColunas = strColunas;
        this.strCelulas = strCelulas;
        this.celulas = new LinkedHashMap<>(this.linhas.size());
        for (L linha : this.linhas) {
            Map<C, X> valoresLinha = new LinkedHashMap<>(this.colunas.size());
            for (C coluna : this.colunas) {
                valoresLinha.put(coluna, null);
            }
            this.celulas.put(linha, valoresLinha);
        }
    }

    public void put(L linha, C coluna, X valor) {
        if (!linhas.contains(linha) || !colunas.contains(coluna)) throw new IllegalArgumentException();
        celulas.get(linha).put(coluna, valor);
    }

    public X get(L linha, C coluna) {
        if (!linhas.contains(linha) || !colunas.contains(coluna)) return null;
        return celulas.get(linha).get(coluna);
    }

    public void forEach(TabelaConsumer<? super L, ? super C, ? super X> consumer) {
        for (L linha : linhas) {
            for (C coluna : colunas) {
                consumer.accept(linha, coluna, get(linha, coluna));
            }
        }
    }

    private List<Integer> calcularLargura() {
        List<Integer> lista = new ArrayList<>(colunas.size() + 1);

        int m = 0;
        for (L linha : linhas) {
            int x = strLinhas.apply(linha).length();
            if (x > m) m = x;
        }
        lista.add(m);

        for (C coluna : colunas) {
            int n = strColunas.apply(coluna).length();
            for (L linha : linhas) {
                X celula = get(linha, coluna);
                int x = celula == null ? 0 : strCelulas.apply(celula).length();
                if (x > n) n = x;
            }
            lista.add(n);
        }

        return lista;
    }

    private void imprimirSeparador(StringBuilder sb, int tamanho) {
        sb.append("+-");
        for (int i = 0; i <= tamanho; i++) {
            sb.append('-');
        }
    }

    private void imprimirSeparador(StringBuilder sb, List<Integer> tamanhos) {
        for (int i : tamanhos) {
            imprimirSeparador(sb, i);
        }
        sb.append("+\n");
    }

    private void imprimirCelula(StringBuilder sb, int tamanho, String conteudo) {
        sb.append("| ").append(conteudo);
        for (int i = conteudo.length(); i <= tamanho; i++) {
            sb.append(' ');
        }
    }

    private void imprimirCabecalho(StringBuilder sb, List<Integer> tamanhos) {
        Iterator<Integer> it = tamanhos.iterator();
        imprimirCelula(sb, it.next(), "");
        for (C coluna : colunas) {
            imprimirCelula(sb, it.next(), strColunas.apply(coluna));
        }
        sb.append("|\n");
    }

    private void imprimirLinha(StringBuilder sb, List<Integer> tamanhos, L linha) {
        Iterator<Integer> it = tamanhos.iterator();
        imprimirCelula(sb, it.next(), strLinhas.apply(linha));
        for (C coluna : colunas) {
            X celula = get(linha, coluna);
            imprimirCelula(sb, it.next(), celula == null ? "" : strCelulas.apply(celula));
        }
        sb.append("|\n");
    }

    @Override
    public String toString() {
        List<Integer> tamanhos = calcularLargura();
        int larguraTotal = tamanhos.stream().reduce(0, Integer::sum) + 3 * tamanhos.size() + 2;
        StringBuilder sb = new StringBuilder(larguraTotal * (linhas.size() * 2 + 3));
        imprimirSeparador(sb, tamanhos);
        imprimirCabecalho(sb, tamanhos);
        imprimirSeparador(sb, tamanhos);
        for (L linha : linhas) {
            imprimirLinha(sb, tamanhos, linha);
            imprimirSeparador(sb, tamanhos);
        }
        return sb.toString();
    }
}

This class is generic. Having it, then we will use the Local and Atividade classes to define, as well as the java.time.LocalDate class for the contents of the table:

class Local {
    private final String nome;

    public Local(String nome) {
        this.nome = nome;
    }

    @Override
    public String toString() {
        return nome;
    }
}

class Atividade {
    private final String nome;

    public Atividade(String nome) {
        this.nome = nome;
    }

    @Override
    public String toString() {
        return nome;
    }
}

Finally, the code that uses all this, creating table 1 and transforming it into table 2:

class Teste {

    private static final DateTimeFormatter FMT = DateTimeFormatter
            .ofPattern("dd/MM/uuuu")
            .withResolverStyle(ResolverStyle.STRICT);

    public static void main(String[] args) {

        // Define os locais e atividades.
        Local casa = new Local("Casa");
        Local escola = new Local("Escola");
        Local trabalho = new Local("Trabalho");
        Atividade estudo = new Atividade("Estudo");
        Atividade treino = new Atividade("Treino");
        Atividade apresentacao = new Atividade("Apresentação");

        // Define a estrutura da tabela 1.
        Tabela<Atividade, Local, LocalDate> tabela1 = new Tabela<>(
                Arrays.asList(estudo, treino, apresentacao),
                Arrays.asList(casa, escola, trabalho),
                Object::toString,
                Object::toString,
                ld -> ld.format(FMT)
        );

        // Preenche a tabela 1.
        tabela1.put(estudo, casa, LocalDate.of(2017, 2, 1));
        tabela1.put(estudo, escola, LocalDate.of(2017, 3, 10));
        tabela1.put(estudo, trabalho, LocalDate.of(2017, 1, 1));
        tabela1.put(treino, casa, LocalDate.of(2017, 5, 3));
        tabela1.put(treino, escola, LocalDate.of(2017, 2, 4));
        tabela1.put(treino, trabalho, LocalDate.of(2017, 5, 1));
        tabela1.put(apresentacao, casa, LocalDate.of(2017, 2, 2));
        tabela1.put(apresentacao, escola, LocalDate.of(2017, 3, 3));
        tabela1.put(apresentacao, trabalho, LocalDate.of(2017, 12, 10));

        // Mostra a tabela 1.
        System.out.println("Tabela 1:");
        System.out.println(tabela1);

        // Descobre as datas que são linhas da tabela 2.
        Set<LocalDate> datas = new TreeSet<>();
        tabela1.forEach((atividade, local, data) -> datas.add(data));

        // Define a estrutura da tabela 2.
        Tabela<LocalDate, Atividade, Local> tabela2 = new Tabela<>(
                datas,
                Arrays.asList(estudo, treino, apresentacao),
                ld -> ld.format(FMT),
                Object::toString,
                Object::toString
        );

        // Preenche a tabela 2 com base nos dados da tabela 1.
        tabela1.forEach((atividade, local, data) -> tabela2.put(data, atividade, local));

        // Mostra a tabela 2.
        System.out.println("Tabela 2:");
        System.out.println(tabela2);
    }
}

Here's the output:

Tabela 1:
+--------------+------------+------------+------------+
|              | Casa       | Escola     | Trabalho   |
+--------------+------------+------------+------------+
| Estudo       | 01/02/2017 | 10/03/2017 | 01/01/2017 |
+--------------+------------+------------+------------+
| Treino       | 03/05/2017 | 04/02/2017 | 01/05/2017 |
+--------------+------------+------------+------------+
| Apresentação | 02/02/2017 | 03/03/2017 | 10/12/2017 |
+--------------+------------+------------+------------+

Tabela 2:
+------------+----------+----------+--------------+
|            | Estudo   | Treino   | Apresentação |
+------------+----------+----------+--------------+
| 01/01/2017 | Trabalho |          |              |
+------------+----------+----------+--------------+
| 01/02/2017 | Casa     |          |              |
+------------+----------+----------+--------------+
| 02/02/2017 |          |          | Casa         |
+------------+----------+----------+--------------+
| 04/02/2017 |          | Escola   |              |
+------------+----------+----------+--------------+
| 03/03/2017 |          |          | Escola       |
+------------+----------+----------+--------------+
| 10/03/2017 | Escola   |          |              |
+------------+----------+----------+--------------+
| 01/05/2017 |          | Trabalho |              |
+------------+----------+----------+--------------+
| 03/05/2017 |          | Casa     |              |
+------------+----------+----------+--------------+
| 10/12/2017 |          |          | Trabalho     |
+------------+----------+----------+--------------+

See working here on ideone.

    
03.09.2017 / 09:33