Reputation: 348
I want to transform duplicates String[]
to List<String>
where every next duplicated element will have increasing counter after it name.
Example:
Test, TestA, TestB, Test, TestA, TestA, TestB
should be transformed to:
Test, TestA, TestB, Test1, TestA1, TestA2, TestB1
Result must have the same order like input data, only counter is added.
Currently I wrote something like this but I don't think if it elegant and efficient way to make this and I'm looking for more proper way:
String[] header = new String[]{"Test", "TestA", "TestB", "Test", "TestA", "TestA", "TestB"};
List<String> newHeader = new ArrayList<>();
for (String column : header) {
if (!newHeader.contains(column)) {
newHeader.add(column);
} else {
int counter = 1;
String columnIterName = column + counter;
while (newHeader.contains(columnIterName)) {
columnIterName = column + counter;
counter++;
}
newHeader.add(columnIterName);
}
}
return newHeader;
Upvotes: 1
Views: 466
Reputation: 51643
You can simplified to the following :
public class Counter {
public static List<String> someMethod(String[] header){
Map<String, Integer> track = new HashMap<>();
List<String> newHeader = new ArrayList<>();
for(String s : header){
int count = track.compute(s, (k, v) -> (v == null) ? 0 : v + 1);
if(count != 0)
track.put(s + count, 0);
newHeader.add(count == 0 ? s : s + count);
}
return newHeader;
}
public static void main(String[] args) {
someMethod(new String[]{"Test","Test","Test1"}).forEach(System.out::println);
}
}
Upvotes: 0
Reputation: 19575
Since Java 8, Map
has methods which allow to compute values if a key is missing, for example, Map::compute
allows to increment the counter using a BiFunction
:
Update: to handle specific test: "Test", "Test", "Test1"
when a duplicate is created as a result of appending the counter, another method putIfAbsent
should be used.
static List<String> convert(String ... header) {
Map<String, Integer> counters = new HashMap<>();
List<String> result = new ArrayList<>();
for (String column : header) {
int count = counters.compute(column, (k, v) -> v == null ? 0 : v + 1);
String toAdd = column + (count == 0 ? "" : Integer.toString(count));
counters.putIfAbsent(toAdd, 0);
result.add(toAdd);
}
return result;
}
Similar solution using Stream API may look as follows.
Update: here putIfAbsent
is invoked in "cheat mode" (using peek
)
static List<String> convertStream(String ... header) {
Map<String, Integer> counters = new HashMap<>();
return Arrays.stream(header)
.map(column -> column +
(counters.compute(column, (k, v) -> v == null ? 0 : v + 1) > 0
? Integer.toString(counters.get(column)) : "")
)
.peek(column -> counters.putIfAbsent(column, 0))
.collect(Collectors.toList());
}
Tests:
String[][] tests = {
{"Test", "Test", "Test1"},
{"Test", "Test1", "TestA", "TestB", "Test", "Test1", "TestA", "TestA", "TestB"}
};
for (String[] test : tests) {
System.out.println("convert: " + convert(test));
System.out.println("stream : " + convertStream(test));
System.out.println("dreamcr: " + some_method(test));
System.out.println("------------\n");
}
Output:
convert: [Test, Test1, Test11]
stream : [Test, Test1, Test11]
dreamcr: [Test, Test1, Test11]
------------
convert: [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
stream : [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
dreamcr: [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
------------
Upvotes: 1
Reputation:
An ArrayList
does not seem to be the best choice to match the strings, as the searches (.contains
) are not efficient. An trying all suffixes in turn is a big waste. A Dictionary
would be better I guess.
Then the logics is
scan the list
if the string is not in the dictionary, enter it with count 1
else increment its count and append it to the string (in-place in the original list).
Upvotes: 0
Reputation: 51643
Assuming that you only care about the duplicates in the original list then:
If you use a Map to keep track of the duplicates you can simplified your code to:
public static List<String> some_method(){
String[] header = new String[]{"Test", "TestA", "TestB", "Test", "TestA", "TestA", "TestB"};
Map<String, Integer> track = new HashMap<>();
List<String> newHeader = new ArrayList<>();
for(String s : header){
Integer count = track.get(s);
if(count != null){
count++;
track.put(s, count);
newHeader.add(s + count);
}
else{
track.put(s, 0);
newHeader.add(s);
}
}
return newHeader;
}
With this you also avoid the inner while loop.
If you also care about the duplicates on the list being build, for instance for the input:
Test Test Test1
you expect the output:
Test Test1 Test11
then you can use the following:
public static List<String> some_method(){
String[] header = new String[]{"Test","Test","Test1"};
Map<String, Integer> track = new HashMap<>();
List<String> newHeader = new ArrayList<>();
for(String s : header){
Integer count = track.get(s);
if(count != null){
count++;
track.put(s, count);
newHeader.add(s + count);
track.put(s + count, 0);
}
else{
track.put(s, 0);
newHeader.add(s);
}
}
return newHeader;
}
Upvotes: 3