Comparar Strings - Problema com a funcao

23 respostas
D

Oi Pessoal,
escrevi um programa no qual leio dois documentos ( csv A e B ) e comparo os Srtings ( palavras )e conto quantas vezes os Strings são parecidos ou iguais.

O documento A contém duas colunas com quase 1 milhão de produtos:
Ex:
número,categoria
01, inverno|malha|sueter

O Documento B contém a mesma estrutura (número e categoria), com as categorias separadas.
Ex.:
Número, categoria
01, malha
02, baby
03, sueter vermelho
04, sueter amarelo

No meu programa ler uma linha do documento A, separa as palavras e compara cada palavra com todas as categorias do documento B e conta quantas vezes a palavra foi encontrada e lava o resultado em um Documento C (csv )
Ex.:.:
id, categoria, palavras encontrada, número
01, inverno |malha |sueter, malha sueter,1|2.
Estou tendo um problema na hora de comparar e contar o número das palavras encontrada.

Atualmente estou usando 2 for para pegar todas as categorias do csv A (CategoryList) e do csv B ( List< Category>) e salvo o a categoria encontrada e o número em uma lista.

O problema é que eu só quero que a palavra encontrada só seja salva uma vez, e que o número só seja somado para essa palavra encontrada e não para todas palavras.

Alguém pode me ajudar pro favor?

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {
            List<Integer> count = new ArrayList<Integer>();
            
            List<String> name = new ArrayList<String>();
                for (String category : cl.getCategories()) {
                    int number= 0;
                    for (Category cat : categories) {
                        if (cat.getName().contains(category)) {
                            name.add(category.toString());
                            number++;
                            count.add(number);
 
 
 
                        }
                    }
                }

23 Respostas

Bruno_Cunha

Não seria mais fácil utilizar um Map?

TreeMap<String nome, Integer quantidade>

D

Oi Bruno obrigado pela dica,
mas o TreeMap so iria esta no lugar das duas listas que eu estou usando.
uma para a categoria encontrada e a outra para a quantidade.

<String,Integer> m_treemap = new TreeMap<String,Integer>();

Voce poderia colocar um exmplo?

nel

Oi.

Faça o seguinte, na minha opinião:

1 - Use um Set, ele não vai permitir que tenhas o mesmo objeto (String)
2 - Use o método Collections.frequency. Basta passar a String que desejar e ele vai lhe retornar a quantidade de vezes que essa palavra aparece na lista, ai não precisa usar iteração sobre a lista
3 - Podes remover o objeto da lista após ele ter sido lido (usado o frequency) ou criar um Set separado para adicionar cada palavra comparada por você, assim, você verifica se o Set contém a palavra em questão, se existir, você não pesquisa, caso contrário, adiciona ao Set e depois pesquisa.

Espero ter ajudado.
Abraços.

P.s: senão entendeu ou não foi o que precisava, posta as duas listas que possui com exemplos de como elas veem preenchidas e uma terceira, que seria esse resultado, para ficar bem claro sua necessidade.

ViniGodoy

Também acho que seria melhor usar um map. Veja um exemplo:

String [] items = {"Banana", "Laranja", "Limão", "Laranja", "Banana", "Lima", "Limão", "Laranja"};

Map&lt;String, Integer&gt; contagem = new TreeMap&lt;String, Integer&gt;();
for (String item : items) {
   if (!contagem.contains(item)) {
      contagem.put(item, 0);
   }
   contagem.put(item, contagem.get(item)+1);
}

for (Map.Entry&lt;String, Integer&gt; entry : contagem.entrySet()) {
   System.out.println("Fruta:" + entry.getKey() + " Quantidade:" + item.getValue());
}

A diferença é que o método contains de um map tem performance de O(1) - no caso do TreeMap, O(Log2(n)), contra o método contains do List, que tem de O(n). Por isso, usar o map no lugar do list deve te dar um ganho expressivo de performance, além de dispensar o uso de 2 estruturas.

Como você quer totalizar todos os objetos da lista, o Collections.frequency não é muito adequado. Ele irá percorrer a lista inteira a cada chamada.

D

Oi Bruno,
o treenap funcionou ou pelo menos parece :slight_smile:

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {
			TreeMap<String,Integer> catcount = new TreeMap<String,Integer>();
			
				for (String category : cl.getCategories()) {
					int number= 0;
					for (Category cat : categories) {
						if (cat.getName().contains(category)) {
							number++;
							catcount.put(category.toString(),number);



						}
					}
				}

Alguem tem uma ideia, como eu posso melhorar a velocidade da comparalaçao?

Eu estou usando 2 laços for e por isso acho que esta demorando muito para ler dodos os dados e comparar los.

Obrigado

ViniGodoy

Tá estranho esse seu código. O valor de number não deveria ser por categoria?

nel

Eu também achei bem estranho mas como possivelmente ele leu e ignorou meus comentários, fiquei em silencio.
Posta a classe Category e a CategoryList, senão fica difícil entendermos o que exatamente tu está fazendo, porque particularmente, não vi sentido no seu código Damn.

D

Voces tem razao ( desculpe me a ortografia, estou no exterior)

O meu codigo nao funciona, como deveria.

vou disponibilizar o meu codico, para voces entenderem melhor.

Class: Category

public class Category {

	private final int id;
	private final String name;

	public Category(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}
}

Class: CategoryList

import java.util.List;

public class CategoryList {

	private final int id;
	private final List<String> categories;
	//private final char wörter;

	public CategoryList(int id, List<String> categories) {
		this.id = id;
		this.categories = categories;
		//this.wörter = wörter;
	}

//	public char getWörter() {
//		return wörter;
//	}

	public int getId() {
		return id;
	}

	public List<String> getCategories() {
		return categories;
	}
}

Class: CategoryQuantity

package de.daniel.jcsv.demo;

import java.util.List;
import java.util.TreeMap;

public class CategoryQuantity {

	private final int id;
	private List<String> categories;
	//private final List<Integer>  quantity;
	//private List<String> name;
	private TreeMap<String,Integer> catcount;
	
	//private final char wörter;

	public TreeMap<String, Integer> getCatcount() {
		return catcount;
	}

	public void setCatcount(TreeMap<String, Integer> catcount) {
		this.catcount = catcount;
	}

	public CategoryQuantity(int id, List<String> categories, TreeMap<String,Integer> catcount2) {
		this.id = id;
		this.categories = categories;
	//	this.quantity = quantity;
		//this.name = name;
		this.catcount =catcount2;
		//this.wörter = wörter;
	}

//	public char getWörter() {
//		return wörter;
//	}

//	public List<String> getName() {
//		return name;
//	}
//
//	public void setName(List<String> name) {
//		this.name = name;
//	}

	public List<String> getCategories() {
		return categories;
	}
	
	public int getId() {
		return id;
	}

//	public List<Integer> getQuantity() {
//		return quantity;
//	}
}

Class: Main ( A principal)

package de.daniel.jcsv.demo;

import java.io.FileNotFoundException;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;

//import org.apache.commons.lang.StringUtils; 

import de.eikeb.jcsv.CSVStrategy;
import de.eikeb.jcsv.reader.CSVEntryParser;
import de.eikeb.jcsv.reader.CSVReader;
import de.eikeb.jcsv.reader.internal.CSVReaderBuilder;
import de.eikeb.jcsv.util.CSVUtil;
import de.eikeb.jcsv.writer.CSVEntryConverter;
import de.eikeb.jcsv.writer.CSVWriter;
import de.eikeb.jcsv.writer.internal.CSVWriterBuilder;

public class Main {
	TreeMap<String,Integer> catcount = new TreeMap<String,Integer>();
	private static CSVStrategy strategy = new CSVStrategy(';', '"', '#', true, true);

	public static void main(String[] args) throws IOException {
		List<Category> categories = readCategories("C:/c.csv");

		// read list A and write quantities
		CSVReader<CategoryList> categoryListReader = buildCategoryListReader("C:/A.csv");
		CSVWriter<CategoryQuantity> categoryQuantityWriter = buildCSVWriter("/ErgContains.csv");

		for (CategoryList cl : categoryListReader) {
			CategoryQuantity categoryQuantity = countCategories(categories, cl);
			categoryQuantityWriter.write(categoryQuantity);
			categoryQuantityWriter.flush();

		}

		categoryQuantityWriter.close();
		categoryListReader.close();
	}
	

		private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {
			TreeMap<String,Integer> catcount = new TreeMap<String,Integer>();
			
			//List<Integer> count = new ArrayList<Integer>();
			
			//List<String> name = new ArrayList<String>();
				for (String category : cl.getCategories()) {
					int number= 0;
					for (Category cat : categories) {
						if (cat.getName().contains(category)) {
							//name.add(category.toString());
							number++;
							catcount.put(category.toString(),number);



						}
					}
				}
		return new CategoryQuantity(cl.getId(), cl.getCategories(), catcount);
	}

	private static CSVWriter<CategoryQuantity> buildCSVWriter(String file) throws IOException {
		String userHome = System.getProperty("user.home");
		Writer out = new FileWriter(userHome + file);
		CSVWriter<CategoryQuantity> csvWriter = new CSVWriterBuilder<CategoryQuantity>(out).strategy(strategy)
				.entryConverter(new CategoryQuantityEntryConverter()).build();
		return csvWriter;
	}

	private static CSVReader<CategoryList> buildCategoryListReader(String file) throws FileNotFoundException {
		//Reader csvFile = new InputStreamReader(Main.class.getResourceAsStream(file));
		Reader csvFile = new FileReader(file);
		CSVReader<CategoryList> csvReader = new CSVReaderBuilder<CategoryList>(csvFile).strategy(strategy)
				.entryParser(new CategoryListEntryParser()).build();
		return csvReader;
	}

	private static List<Category> readCategories(String file) throws IOException {
		//Reader csvFile = new InputStreamReader(Main.class.getResourceAsStream(file));
		Reader csvFile = new FileReader(file);
		CSVReader<Category> csvReader = new CSVReaderBuilder<Category>(csvFile).strategy(strategy)
				.entryParser(new CategoryEntryParser()).build();

		List<Category> articles = csvReader.readAll();
		csvReader.close();

		return articles;
	}

	private static class CategoryEntryParser implements CSVEntryParser<Category> {
		@Override
		public Category parseEntry(String... data) {
			if (data.length != 2) {
				throw new IllegalArgumentException("data is not a valid article");
			}
			int id = Integer.parseInt(data[0]);
			String name = data[1];

			return new Category(id, name);
		}
	}

	private static class CategoryListEntryParser implements CSVEntryParser<CategoryList> {
		@Override
		public CategoryList parseEntry(String... data) {
			if (data.length != 2) {
				throw new IllegalArgumentException("data is not a valid article");
			}
			int id = Integer.parseInt(data[0]);
			List<String> categories = Arrays.asList(data[1].split("[\\|\\||\\|||\\;\\/\\-\\>\\s+]"));
						
			CategoryList wrt = null; 
			return new CategoryList(id, categories);
		}
	}

	private static class CategoryQuantityEntryConverter implements CSVEntryConverter<CategoryQuantity> {
		@Override
		public String[] convertEntry(CategoryQuantity e) {
			String[] data = new String[3];
			data[0] = String.valueOf(e.getId());
			//data[1] = String.valueOf(e.getCategories());
			data[1] = CSVUtil.implode(e.getCategories().toArray(new String[0]), " | ");
			data[2] = String.valueOf(e.getCatcount());
			//data[3] = String.valueOf(e.getQuantity());
			return data;
		}
		
//		public String getword(){
//			CategoryQuantity e = null;
//			if(e.getQuantity() == 0)
//				return " Wort";
//			
//			else 
//				return " Wörter";
//		}
	}
}
D

Espero que voces possam me ajudar. Já tentei varias coisas, mas chego ao resultado desejado.

Desde já agradeco

nel

Certo.

Veja, é uma idéia:

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {  
            Map<String,Integer> result = new TreeMap<String,Integer>();  
            Set<String> control = new HashSet<String>();
            List<String> categories = cl.getCategories();
            
            for (Category category : categories) {
                if(control.contains(category.getName()) 
                    continue;
                
                control.add(category.getName());
                result.put(category.getName(), Collections.frequency(categories, category.getName()); 
            }

            return new CategoryQuantity(cl.getId(), cl.getCategories(), result);  
    }

Estou percorrendo minha lista de categorias e para cada categoria lida eu vejo se existe a mesma na lista de controle. Se existir, eu já contei ela senão eu conto e adiciono. Para contar, só uso o Collections.frequency. Era isso ? Ficou como queria ?

Porque na realidade, fiz o código aqui na página, sem testes.

D

Na linha 4 eu recebo o erro : duplicate variable categories.
-> se eu mudar o nome para categories1
cannot convert from element type String to Category ( na linha 5 )

help !

nel

Damn:
Na linha 4 eu recebo o erro : duplicate variable categories.
-> se eu mudar o nome para categories1
cannot convert from element type String to Category ( na linha 5 )

help !

É só gastar um fosfato colega. Eu troquei os nomes ali, então coloca como categoryList, assim não duplica.
Fora isso não é para dar nenhum erro. Se der, aponta a linha correta porque a 4 está vazia.

D

Entao eu devo estar com bloquei na minha logica.
Ainda nao entendi, talvez porque seja um iniciante.
Como eu escrevi, se eu muda o nome na linha 4 ele acusa ( na linha 6) que ele nao pode transformar um String em um Category.

Como falei sou iniciante, já tentei muito, mas nao consegui resolver esse problema

D

Nel, no inicio eu estava usando dois lacos for. Para ler as categiroas dos dois CSV documentos.

Com o atual codigo eu so estou lendo as categorias do CSV A (CategoryList cl ), mas eu ano estou comprarando com as categorias do CSV B (List categories)

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {    
        Map<String,Integer> result = new TreeMap<String,Integer>();    
        Set<String> control = new HashSet<String>();  
        List<String> categories1 = cl.getCategories();  
 
        for (String category : categories1) {  
            if(control.contains(category.toString()))
                continue;  
              
            control.add(category.toString());  
            result.put(category.toString(), Collections.frequency(categories1, category.toString()));   
        }  

        return new CategoryQuantity(cl.getId(), cl.getCategories(), result);    
}
nel

Está sim. Na realidade, você leu o que faz o método Collections.frequency()? Ele retorna a quantidade de objetos daquele tipo, encontrados na lista.

Bom, para não adicionar nenhum 0, podes só fazer um simples if antes de adicionar ao Map de resultados.

if (categories1.contains(category)) { // adiciona ao map }

De qualquer forma, tu vai adicionar a lista de controle, pois você está fazendo a verificação de um novo valor, esse if é antes de adicionar ao map (o .put).
Outra, porque fazer um toString() em uma String ? É igual a fazer um parser em um Integer para um Integer, ou seja, sem sentido.

D

Em relaco ao toString voce tem razao. Eu ja mudei o meu code.

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {    
        Map<String,Integer> result = new TreeMap<String,Integer>();    
        Set<String> control = new HashSet<String>();  
        List<String> categories1 = cl.getCategories();  
        
        for (String category : categories1) {  
         	            
            if(control.contains(category))
                continue;  
              
            control.add(category);  
            result.put(category, Collections.frequency(categories1, category)); 
        	
        }  

        return new CategoryQuantity(cl.getId(), cl.getCategories(), result);    
}

Sim eu li o que o frequency faz, mas em nelhum ponto eu estou lendo o List categories e fazendo uma comparacao com o CategoryList cl.

Antes eu comparava as categorias dessa forma:

if (cat.getName().contains(category)) .

Voce poderia posta o code que voce esta pensando, por favor

nel
private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {      
    Map<String,Integer> result = new TreeMap<String,Integer>();      
    Set<String> control = new HashSet<String>();    
    List<String> categories1 = cl.getCategories();    
      
    for (String category : categories1) {    
                      
        if(control.contains(category))  
            continue;    
            
        control.add(category);    
        if(categories1.contains(category) {
            result.put(category, Collections.frequency(categories1, category));   
        }          
    }      
    return new CategoryQuantity(cl.getId(), cl.getCategories(), result);

Pronto.

D

Oi nel, eu acho que voce nao entendeu o que eu quiz dizer.

O meu problem é:

  1. Ler Category> categories e CategoryList cl
  2. comparar essas duas categorias ( categories sao as categorias do CSV B e cl contem as categorias do CSV A)
  3. contar quantas vezes, cada categria do CSV A no CSV B existe.
  4. Salvar o resultado em um CSV.

O problema, é que com o ultimo code que vc postou, eu nao conparo as do CSV B com o A.

Na verdade o CSV B nao esta sendo lido e sem sendo comparado :frowning:

nel

Damn:
Oi nel, eu acho que voce nao entendeu o que eu quiz dizer.

O meu problem é:

  1. Ler Category> categories e CategoryList cl
  2. comparar essas duas categorias ( categories sao as categorias do CSV B e cl contem as categorias do CSV A)
  3. contar quantas vezes, cada categria do CSV A no CSV B existe.
  4. Salvar o resultado em um CSV.

O problema, é que com o ultimo code que vc postou, eu nao conparo as do CSV B com o A.

Na verdade o CSV B nao esta sendo lido e sem sendo comparado :(

Mas também né, não sei se postei assim da primeira vez mas é só perceber que está percorrendo e verificando sempre a mesma lista, obviamente isso não está certo.
É só alterar o for ali, para:

for (Category cat : categories) { String category = cat.getName(); //o resto é igual }

Pronto. Agora será comparado. Tens que parar, pensar, analisar, parece-me que está fazendo com pressa, sem analisar o problema.

D

Obrigado pelas dicas, mas o code nao funciona e alem disso ele, o programa ficor lendo esses lacos por 5 mintos.

Depois parei e olhei o resultados e vi que ainda nao funciona.

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {        
        Map<String,Integer> result = new TreeMap<String,Integer>();        
        Set<String> control = new HashSet<String>();      
        List<String> categories1 = cl.getCategories();      
        
        
      for (Category cat : categories) {     
//        	   String category = cat.getName();  
        for (String category : categories1) {      
        	if(cat.getName().contains(category))
            if(control.contains(category))    
                continue;      
                  
            control.add(category);      
            if(categories1.contains(category))
            		{  
                result.put(category, Collections.frequency(categories1, category));     
            }            
        }  
      }
        
        return new CategoryQuantity(cl.getId(), cl.getCategories(), result);  

    }
nel

Cara, tu não está prestando atenção no que estou lhe dizendo. Você fez dois laços de repetição e ainda comentou o que eu disse para ser feito.
Vou tentar arrumar isso e copia e usa exatamente igual.

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {          
       Map<String,Integer> result = new LinkedHashMap<String,Integer>();          
       Set<String> control = new HashSet<String>();        
       List<String> catList = cl.getCategories();           
          
      for (Category cat : categories) {       
        String category = cat.getName();    
        if(control.contains(category))      
        	continue;        
                    
        control.add(category);        
        if(catList.contains(category)) {    
        	result.put(category, Collections.frequency(catList, category));       
        }   
      }   
      return new CategoryQuantity(cl.getId(), cl.getCategories(), result);

Pronto. COPIA e COLA esse código e não insere código sem sentido como fez anteriormente, por favor. Não é questão de falta de experiência, é que você simplesmente está alterando completamente o sentido das coisas ou fazendo com pressa.

Olha essa linha que tu adicionou:

if(cat.getName().contains(category))

Sabe o que ela faz ? Verifica se uma STRING contém uma outra STRING. Se quer comparar, usa o equals colega, não um contains. Enfim, copia o código que lhe dei e faça o teste. Senão funcionar, posta os resultados para analisarmos o que está errado ! Ok?

Abraços.

D

nel, eu usei o laco. Eu uso o contains por que eu nao quero encontrar apenas lavras iguais, mas se pedacos da palavra.
Ex.:
"roupra" ( nos csv A) para ser comparada com "roupa" e "guarda-roupa" (no csv B), nesse caso as categorias tem que ser aceitas como iguais e somar o resultado assim
roupa=2

Se eu usar equals ela so aceita palavras iguais.

o codigo nao faz o que ele deveria fazer

private static CategoryQuantity countCategories(List<Category> categories, CategoryList cl) {             
	       Map<String,Integer> result = new LinkedHashMap<String,Integer>();             
	       Set<String> control = new HashSet<String>();           
	       List<String> catList = cl.getCategories();             
	             
	      for (Category cat : categories) {         
	        String category = cat.getName();       
	        if(control.contains(category))         
	            continue;           
	                       
	        control.add(category);           
	        if(catList.contains(category)) {       
	            result.put(category, Collections.frequency(catList, category));         
	        }     
	      }     
	      return new CategoryQuantity(cl.getId(), cl.getCategories(), result);      
	}

Por exemplo:

ele pega a palavra "Sporthosen" do csv A e compara com todas as categias do CSV B e conta.
Eu Abri o CSV B e procurei a palavra "Sporthosen", e eu encontro Sporthosen e XXL-Sporthosen. Se o code correto deveria salvar Sporthosen=2, mas ele salva apenas Sporthosen=1

Aqui é o resultado, que é salvo no CSV C
23850467, Textil | Sport | Sporttextilien | Kinder | Unterteile | Sport | Kinder | lange | Sporthosen | (normal | weit), {Sporthosen=1}

nel

Oi.

Eu não li isso anteriormente ou simplesmente não vi. Não tem como fugir muito de dois laços de iteração, pois se é parte de uma palavra tu terá que ficar comparando um palavra da lista A com todas da lista B, até que se encerre o ciclo da A. Pois podemos ter maçã, maçã-verde e por ai em diante. O contains na lista busca o objeto identico ao que é passado, o que eu havia entendido até o momento. Como não é o caso, ou busca uma forma melhor de iterar ou itera sobre as duas listas.

Criado 24 de janeiro de 2012
Ultima resposta 27 de jan. de 2012
Respostas 23
Participantes 4