7
votes

Générer une structure d'arborescence de CSV

J'ai gratté ma tête sur ce problème pendant un moment maintenant. J'essaie essentiellement de générer une hiérarchie des arbres à partir d'un ensemble de données CSV. Les données CSV ne sont pas nécessairement commandées. Ceci est comme quelque chose comme suit:

CSVParser csv = new CSVParser(content);
String[] line;
while((line = csv.readLine()) != null ) {
    ...
}


1 commentaires

Quelle est la grande image? En cas de ligne a, xx, 12,34 Comment savez-vous que 12 et 34 appartiennent à A ou XX?


6 Réponses :


0
votes

basé sur la manière dont ce problème est posé, je ferais ce qui suit:

  1. Définissez ce que votre structure de données finale ressemblera à contenir le Arbre.
  2. Définissez une représentation pour chaque ligne de votre texte original (Peut-être une liste liée pour la flexibilité)
  3. Écrivez une méthode qui prend la ligne représentée et l'insère dans la structure de données d'arborescence. Pour chaque branche inexistante, créez-la; Pour chaque branche existante, traverser la structure de la liste de liens "ligne".
  4. Commencez par un arbre vide.
  5. Lisez chaque ligne du fichier dans la structure de votre ligne d'élément et appelez la méthode définie à l'étape 3.

    Est-ce que cela aide?


0 commentaires

3
votes

L'idée de base n'est pas difficile: groupe par le premier enregistrement, puis par le deuxième enregistrement, etc. Jusqu'à ce que vous obtenez quelque chose comme ceci:

// builds tree and returns a list of root nodes
// list_of_tuples: a list of tuples read from your csv
// curr_position: used to keep track of recursive calls
// number_of_records: assuming each csv row has n records and then m values, number_of_records equals n
function build_tree(list_of_tuples, curr_position, number_of_records) {
    // check if we have already reached the "values" (which shouldn't get converted into trees)
    if (curr_position == number_of_records) {
        return list of nodes, each containing a "value" (i.e. everything from position number_of_records on)
    }

    grouped = group tuples in list_of_tuples that have the same value in position curr_position, and store these groups indexed by such common value
    unique_values = get unique values in curr_position

    list_of_nodes = empty list

   // create the nodes and (recursively) their children
    for each val in unique_values {
        the_node = create tree node containing val
        the_children = build_tree(grouped[val], curr_position+1, number_of_records)
        the_node.set_children(the_children)

        list_of_nodes.append(the_node)
    }

    return list_of_nodes
}

// in your example, this returns a node with "A" and a node with "B"
// third parameter is 2 because you have 2 "records"
build_tree(list_parsed_from_csv, 0, 2)


2 commentaires

Merci pour la pseudo pensée.


@Andez Désolé pour aucun vrai code Java, je pensais juste qu'un croquis serait plus approprié à ce stade afin de se concentrer sur l'algorithme. Notez que c'est la seule solution jusqu'à présent qui gère un nombre arbitraire de niveaux d'enregistrements. Si vous souhaitez traduire le code en Java (ou quiconque souhaite poster une réponse Java en fonction de ce que j'ai posté) Je serai heureux d'aider.



1
votes

Si vous savez que vous n'aurez que deux niveaux de enregistrement s, j'utiliserais quelque chose comme xxx

lorsque vous lisez une nouvelle ligne, vous regardez dans le Carte extérieure pour vérifier si cette valeur pour record1 existe déjà et sinon, créez une nouvelle carte intérieure vide pour cela.

Vérifiez que la carte interne soit Une valeur pour ce record2 existe. Sinon, créez une nouvelle liste .

puis lisez les valeurs et ajoutez-les à la liste.


1 commentaires

C'est la chose, j'aurai plus de colonnes dépendantes des fichiers CSV que je traite. J'ai pris en compte les cartes d'abord. Merci anez



2
votes

Voici la solution de base de base sous la forme de Junit (aucune assertion, cependant) simplifiée en utilisant Collections Google-Guava . Le code est explicite auto-explicite et au lieu de fichier IO, vous utilisez des bibliothèques CSV pour la lecture du CSV. Cela devrait vous donner l'idée de base.

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.junit.Test;

import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.io.Files;

public class MyTest
{
    @Test
    public void test1()
    {
        List<String> rows = getAllDataRows();

        Multimap<Records, Values> table = indexData(rows);

        printTree(table);

    }

    private void printTree(Multimap<Records, Values> table)
    {
        Set<String> alreadyPrintedRecord1s = Sets.newHashSet();

        for (Records r : table.keySet())
        {
            if (!alreadyPrintedRecord1s.contains(r.r1))
            {
                System.err.println(r.r1);
                alreadyPrintedRecord1s.add(r.r1);
            }

            System.err.println("\t" + r.r2);

            Collection<Values> allValues = table.get(r);

            for (Values v : allValues)
            {
                System.err.println("\t\t" + v.v1 + " , " + v.v2);
            }
        }
    }

    private Multimap<Records, Values> indexData(List<String> lines)
    {
        Multimap<Records, Values> table = ArrayListMultimap.create();

        for (String row : lines)
        {
            Iterable<String> split = Splitter.on(",").split(row);
            String[] data = Iterables.toArray(split, String.class);

            table.put(new Records(data[0], data[1]), new Values(data[2], data[3]));
        }
        return table;
    }

    private List<String> getAllDataRows()
    {
        List<String> lines = Collections.emptyList();

        try
        {
            lines = Files.readLines(new File("C:/test.csv"), Charsets.US_ASCII);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        lines.remove(0);// remove header

        return lines;
    }
}



public class Records
{
    public final String r1, r2;

    public Records(final String r1, final String r2)
    {
        this.r1 = r1;
        this.r2 = r2;
    }

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((r1 == null) ? 0 : r1.hashCode());
        result = prime * result + ((r2 == null) ? 0 : r2.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj == null)
        {
            return false;
        }
        if (!(obj instanceof Records))
        {
            return false;
        }
        Records other = (Records) obj;
        if (r1 == null)
        {
            if (other.r1 != null)
            {
                return false;
            }
        }
        else if (!r1.equals(other.r1))
        {
            return false;
        }
        if (r2 == null)
        {
            if (other.r2 != null)
            {
                return false;
            }
        }
        else if (!r2.equals(other.r2))
        {
            return false;
        }
        return true;
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        builder.append("Records1and2 [r1=").append(r1).append(", r2=").append(r2).append("]");
        return builder.toString();
    }

}


public class Values
{
    public final String v1, v2;

    public Values(final String v1, final String v2)
    {
        this.v1 = v1;
        this.v2 = v2;
    }

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((v1 == null) ? 0 : v1.hashCode());
        result = prime * result + ((v2 == null) ? 0 : v2.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj == null)
        {
            return false;
        }
        if (!(obj instanceof Values))
        {
            return false;
        }
        Values other = (Values) obj;
        if (v1 == null)
        {
            if (other.v1 != null)
            {
                return false;
            }
        }
        else if (!v1.equals(other.v1))
        {
            return false;
        }
        if (v2 == null)
        {
            if (other.v2 != null)
            {
                return false;
            }
        }
        else if (!v2.equals(other.v2))
        {
            return false;
        }
        return true;
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        builder.append("Values1and2 [v1=").append(v1).append(", v2=").append(v2).append("]");
        return builder.toString();
    }

}


1 commentaires

Merci, a l'air intéressant. Va regarder dans ça.



1
votes

J'ai récemment eu besoin de faire à peu près la même chose et a écrit Tree-Builder.com pour accomplir la tâche. La seule différence est que, comme vous avez votre CSV aménagée, les deux derniers paramètres seront parents et enfants au lieu de pairs. En outre, ma version n'accepte pas une ligne d'en-tête.

Le code est tout en JavaScript; Il utilise Jstree pour construire l'arborescence. Vous pouvez utiliser Firebug ou simplement voir la source sur la page pour voir comment cela est fait. Il serait probablement assez facile de le modifier pour échapper à la virgule de votre CSV afin de garder les deux derniers paramètres est un seul enfant.


0 commentaires

1
votes
    public static void main (String arg[]) throws Exception
{
    ArrayList<String> arRows = new ArrayList<String>();
    arRows.add("A,XX,22,33");
    arRows.add("A,XX,777,888");
    arRows.add("A,YY,33,11");
    arRows.add("B,XX,12,0");
    arRows.add("A,YY,13,23");
    arRows.add("B,YY,44,98");
    for(String sTreeRow:createTree(arRows,",")) //or use //// or whatever applicable
        System.out.println(sTreeRow);
}
    public static ArrayList<String> createTree (ArrayList<String> arRows, String sSeperator) throws Exception
{
    ArrayList<String> arReturnNodes = new ArrayList<String>();
    Collections.sort(arRows);
    String sLastPath = "";
    int iFolderLength = 0;
    for(int iRow=0;iRow<arRows.size();iRow++)
    {
        String sRow = arRows.get(iRow);
        String[] sFolders = sRow.split(sSeperator);
        iFolderLength = sFolders.length;
        String sTab = "";
        String[] sLastFolders = sLastPath.split(sSeperator);
        for(int i=0;i<iFolderLength;i++)
        {
            if(i>0)
                sTab = sTab+"    ";
            if(!sLastPath.equals(sRow))
            {

                if(sLastFolders!=null && sLastFolders.length>i)
                {
                    if(!sLastFolders[i].equals(sFolders[i]))
                    {
                        arReturnNodes.add(sTab+sFolders[i]+"");
                        sLastFolders = null;
                    }
                }
                else
                {
                    arReturnNodes.add(sTab+sFolders[i]+"");
                }
            }
        }
        sLastPath = sRow;
    }
    return arReturnNodes;
}

0 commentaires