Get List of Integers from array of Strings - java

I would like to make my code look more efficient. Currently I got something like this:
private static String[] getAllFilesFromDirectory(String path) {
File file = new File(path);
String fileNamePattern = file.getName() + ".*\\.[0-9]*\\.GOOD";
File dir = new File(file.getParent());
String[] files = dir.list((currDir, currName) -> currName.matches(fileNamePattern));
return files;
}
private static List<Integer> getAllFileNumbers(String path) {
String[] files = getAllFilesFromDirectory(path);
List<Integer> fileNumbers = new ArrayList<>();
for (String currentFile : files) {
fileNumbers.add(extractFileNumber(currentFile));
}
return fileNumbers;
}
private static Integer extractFileNumber(String fileName) {
return Integer.parseInt(fileName.split("\\.")[2]);
}
First thing first. To getAllFileNumbers method I am passing path to directory, after that I am getting array of files in this directory with specific pattern. And from every file I need to extract number.
Example:
test.txt.1.fail
test2.txt.2.good
test3.pdf.1.good
Here I am returning list of following numbers : 1, 2, 1.
What I would like to change? Probably use stream api instead of foreach loop.
First idea was something like this:
Arrays.stream(files).map(this::extractFileNumber).collect(Collectors.toList());
But because the methods are static i cant use it like this.
Obviously I could move extractFileNumber inside of lambda but I would like to keep my method separately.
Maybe someone has other ideas, please feel free to post any ideas. Cheers!

Use stream api can make it shorter, suppose extractFileNumber is in class Main:
return Stream.of(files).map(Main::extractFileNumber).collect(Collectors.toList());

Related

How can i return a ArrayList and a File Array from one method?

I want to return a ArrayList and a File Array with one method. For more Background(isnt necessary to help me) you can read the rest.
I am coding my version for the Sokoban game. For that i have to get a List(in my case a ArrayList) with all the paths of the levels so i can get the information and I also need the File Array because i used in a previous version and it would take hours to change that. The input for the method is "level" at the moment. Thats the directory with the .xsd files.r
I cant rly change the things i want to return.
public static ArrayList<SokobanLevel> loadLevels(String dirPath) throws SokobanException {
Path dir =
FileSystems.getDefault().getPath(dirPath).toAbsolutePath();
File[] fileFolder = dir.toFile().listFiles();
ArrayList<SokobanLevel> allLevel = new ArrayList<SokobanLevel>();
for(int i = 0; i < fileFolder.length; i++) {
SokobanLevel sl = new
SokobanLevel(fileFolder[i].getAbsolutePath());
sl.setId(i);
allLevel.add(sl);
}
//would like to also return File[] fileFolder
return allLevel;
}
method works so far so good.
Just make a tuple like this:
Class Result {
public static final ArrayList list;
public static final File[] files;
public Result(ArrayList list,File[] files){
this.list =list;
this.files = files;
}
}
And use it like this:
return new Result(list,files)
When you need to use the list:result.list.
When you need to use the files:result.files.
It's read-only because it's qualifies wiht final keyword.
You cannot return multiple objects from one method. You can put the different objects in an new class and then return that.

JUnit - Java - How to test void method with List of file as parameter

I have a class with this method inside:
public class DirectoryWalker {
public void visitDirectory(String path,List<File> resultList ) {...}
...
}
I want to test the method, and I started to do it as showed below.
#Test
public void ListOfTheFiles(){
List<File> result = directoryWalker.visitDirectory("pathFile", new ArrayList<File>());
Assert.assertThat("path\File\\0A54-4444-2441-D554.xml", is(result));
To be honest I am little bit confused because this is the first time that I try to test a void method with a list as parameter.
I expect this file:
"path\File\\0A54-4444-2441-D554.xml
into the folder File
Could you help me please? Thanks
EDIT: Of course this istruction is wrong because result is inizialized as List of File:
List<File> result = directoryWalker.visitDirectory(...);
All the class:
public class DirectoryWalker {
public void visitDirectory(String path, List<File> resultList ) {
File root = new File(path);
File[] list = root.listFiles();
for (File f : list) {
if (f.isFile() && f.getName().toLowerCase().endsWith(".xml")) {
resultList.add(f);
} else if(f.isDirectory()) {
visitDirectory(f.getAbsolutePath(),resultList);
}
}
}
}
Assert.assertThat("path\File\\0A54-4444-2441-D554.xml", is(result));
You are using the assert in a wrong way. The patter is:
assertThat([ACTUAL_RESULT], [matcher]([EXPECTED_RESULT]))
You doing it the other way around.
Then, if the expected result is a List, then why do you compare it to a String?
it should be like this:
#Test
public void ListOfTheFiles(){
List<File> result = new ArrayList<>();
directoryWalker.visitDirectory("pathFile", result);
Assert.assertThat(result, hasItem(new File("path\File\\0A54-4444-2441-D554.xml")));
BTW:
avoid backslashes (\) in hardcoded file paths. Use forward slashes (/) instead since this ensures compatibility to non windows System which is one major reason to use Java (and not C#)...
Testing a void method is like testing if what it should done is done properly. You cannot do such thing like result = class.method(...), because this method does not return any value.
What does this method do? It could be helpful...

Sort files in numeric order

I made a program to combine all files in a folder together.
Here's part of my code:
File folder = new File("c:/some directory");
File[] listOfFiles = folder.listFiles();
for (File file : listOfFiles){
if (file.isFile()){
System.out.println(file.getName());
File f = new File("c:/some directory"+file.getName());
However, I hope my files can be in order of like:
job1.script, job2.script, .....
but I get:
job1.script, job10.script, job11.script, that 10,11,12... are in front of 2.
I hope I can get efficient code that can avoid this problem.
Time to get rid of all the clumpsy code, and use Java 8! This answer also features the Path class, which is already part of Java 7, however seems to be heavily improved in Java 8.
The code:
private void init() throws IOException {
Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
Files.list(directory)
.filter(path -> Files.isRegularFile(path))
.filter(path -> path.getFileName().toString().startsWith("job"))
.filter(path -> path.getFileName().toString().endsWith(".script"))
.sorted(Comparator.comparingInt(this::pathToInt))
.map(path -> path.getFileName())
.forEach(System.out::println);
}
private int pathToInt(final Path path) {
return Integer.parseInt(path.getFileName()
.toString()
.replace("job", "")
.replace(".script", "")
);
}
The explanation of pathToInt:
From a given Path, obtain the String representation of the file.
Remove "job" and ".script".
Try to parse the String as an Integer.
The explanation of init, the main method:
Obtain a Path to the directory where the files are located.
Obtain a lazily populated list of Paths in the directory, be aware: These Paths are still fully qualified!
Keep files that are regular files.
Keep files of which the last part of the Path, thus the filename (for example job1.script) starts with "job". Be aware that you need to first obtain the String representation of the Path before you can check it, else you will be checking if the whole Path starts with a directory called "job".
Do the same for files ending with ".script".
Now comes the fun point. Here we sort the file list based on a Comparator that compares the integers which we obtain by calling pathToInt on the Path. Here I am using a method reference, the method comparingInt(ToIntFunction<? super T> keyExtractor expects a function that maps a T, in this case a Path, to an int. And this is exactly what pathToInt does, hence it can be used a method reference.
Then I map every Path to the Path only consisting of the filename.
Lastly, for each element of the Stream<Path>, I call System.out.println(Path.toString()).
It may seem like this code could be written easier, however I have purposefully written it more verbose. My design here is to keep the full Path intact at all times, the very last part of the code in the forEach actually violates that principle as shortly before it gets mapped to only the file name, and hence you are not able to process the full Path anymore at a later point.
This code is also designed to be fail-fast, hence it is expecting files to be there in the form job(\D+).script, and will throw a NumberFormatException if that is not the case.
Example output:
job1.script
job2.script
job10.script
job11.script
An arguably better alternative features the power of regular expressions:
private void init() throws IOException {
Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
Files.list(directory)
.filter(path -> Files.isRegularFile(path))
.filter(path -> path.getFileName().toString().matches("job\\d+.script"))
.sorted(Comparator.comparingInt(this::pathToInt))
.map(path -> path.getFileName())
.forEach(System.out::println);
}
private int pathToInt(final Path path) {
return Integer.parseInt(path.getFileName()
.toString()
.replaceAll("job(\\d+).script", "$1")
);
}
Here I use the regular expression "job\\d+.script", which matches a string starting with "job", followed by one or more digits, followed by ".script".
I use almost the same expression for the pathToInt method, however there I use a capturing group, the parentheses, and $1 to use that capturing group.
I will also provide a concise way to read the contents of the files in one big file, as you have also asked in your question:
private void init() throws IOException {
Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
try (BufferedWriter writer = Files.newBufferedWriter(directory.resolve("masterjob.script"))) {
Files.list(directory)
.filter(path -> Files.isRegularFile(path))
.filter(path -> path.getFileName().toString().matches("job\\d+.script"))
.sorted(Comparator.comparingInt(this::pathToInt))
.flatMap(this::wrappedLines)
.forEach(string -> wrappedWrite(writer, string));
}
}
private int pathToInt(final Path path) {
return Integer.parseInt(path.getFileName()
.toString()
.replaceAll("job(\\d+).script", "$1")
);
}
private Stream<String> wrappedLines(final Path path) {
try {
return Files.lines(path);
} catch (IOException ex) {
//swallow
return null;
}
}
private void wrappedWrite(final BufferedWriter writer, final String string) {
try {
writer.write(string);
writer.newLine();
} catch (IOException ex) {
//swallow
}
}
Please note that lambdas cannot throw/catch checked Exceptions, hence there is a neccessity to write wrappers around the code, that decides what to do with the errors. Swallowing the exceptions is rarely a good idea, I am just using it here for code simplicitely.
The real big change here is that instead of printing out the names, I map every file to its contents and write those to a file.
If your files' name are always like jobNumber.script you could sort the array providing a custom comparator:
Arrays.sort(listOfFiles, new Comparator<File>(){
#Override
public int compare(File f1, File f2) {
String s1 = f1.getName().substring(3, f1.getName().indexOf("."));
String s2 = f2.getName().substring(3, f2.getName().indexOf("."));
return Integer.valueOf(s1).compareTo(Integer.valueOf(s2));
}
});
public static void main(String[] args) throws Exception{
File folder = new File(".");
File[] listOfFiles = folder.listFiles(new FilenameFilter() {
#Override
public boolean accept(File arg0, String arg1) {
return arg1.endsWith(".script");
}
});
System.out.println(Arrays.toString(listOfFiles));
Arrays.sort(listOfFiles, new Comparator<File>(){
#Override
public int compare(File f1, File f2) {
String s1 = f1.getName().substring(3, f1.getName().indexOf("."));
String s2 = f2.getName().substring(3, f2.getName().indexOf("."));
return Integer.valueOf(s1).compareTo(Integer.valueOf(s2));
}
});
System.out.println(Arrays.toString(listOfFiles));
}
Prints:
[.\job1.script, .\job1444.script, .\job4.script, .\job452.script, .\job77.script]
[.\job1.script, .\job4.script, .\job77.script, .\job452.script, .\job1444.script]
The easiest solution is to zero pad all digits lower than 10. Like
job01.script
instead of
job1.script
This assumes no more than 100 files. With more, simply add more zeros.
Otherwise, you'll need analyze and breakdown each file name, and then order it numerically. Currently, it's being ordered by character.
The simplest method to solve this problem is to prefix your names with 0s. This is what I did when I had the same problem. So basically you choose the biggest number you have (for example 433234) and prefix all numbers with biggestLength - currentNumLength zeroes.
An example:
Biggest number is 12345: job12345.script.
This way the first job becomes job00001.script.

I want to find a value from an array

Here's my java code:
import java.io.File;
import java.util.Arrays;
public class mainClass {
public static void main(String... args) {
File[] files = new File("%appdata%").listFiles();
showFiles(files);
System.out.println( Arrays.toString( files ) );
if (Arrays.asList(files).contains(".minecraft")) {
System.out.println("Success!");
}
}
public static void showFiles(File[] files) {
}
}
I want code above to check if .minecraft folder exists in %appdata%. I am total N00B to Java. I have worked with PHP, but doesn't seem to help me :) Please help, it annoys me.
-Simon
If you are interested in finding only the ".minecraft" file it would be much easier to:
File appdata = new File("%appdata%");
File minecraft = new File(appdata, ".minecraft");
if (minecraft.exists()) {
System.out.println("Success");
}
EDIT: Based on comment, (and I'm a linux guy mostly), you need to use the correct %APPDATA% location: How do I get the value of Windows' %APPDATA% location variable in Java?
The problem is that .minecraftis a hidden folder. You need to access the folder like this:
File directory = new File("%appdata%");
File[] hiddenFiles = directory.listFiles((FileFilter) HiddenFileFilter.HIDDEN);
for (File hiddenFile: hiddenFiles) {
System.out.println("hidden file: " + hiddenFile.getCanonicalPath());
}
As rolfl mentioned, there is a better way to look for a single file.
That said, your code isn't performing a proper check. You are creating an array of File objects, converting the array to a List, and then checking the list for a String value. The String value will never match a File object.
If you want to find a single file, use rolfl's answer. If you want to fix your code specifically, here's something to get your started:
You need to iterate over the list of files. What do you gain by converting to a List?
You need to find a way to match a File's name with a String name. What method might you call on the File object to get its name?
You need to do a String comparison between the File's name and ".minecraft". What might that comparison look like?
Please note: reference L.Butz answer as well; I haven't accessed hidden files in Java, so it's possible there's an extra step you need to get access to them.
%appdata%is an environment variable and as such it will not be automatically resolved by File. So you need to resolve it before listing it. This is done using System#getenv
#Test
public void dirExistsInAppData() {
Assert.assertTrue(dirExistsInAppData(".minecraft"));
}
private boolean dirExistsInAppData(final String dirname) {
File dir = new File(System.getenv("APPDATA"), dirname);
return dir.exists() && dir.isDirectory();
}

Is there any easy way within Java to prefix one string onto multiple other strings?

Does anybody know if there is any easy way within Java to prefix one string onto multiple other strings?
For example, if I have the following snippet of Java code ;
String includeDir = "/usr/local/boost-1.52.0/include";
ArrayList<String> filenamesRelative = new ArrayList<String>(),
filenamesAbsolute = new ArrayList<String>();
filenamesRelative.add("/boost/aligned_storage.hpp");
filenamesRelative.add("/boost/any.hpp");
I would like to be able to prefix the value of the variable 'includeDir', i.e. "/usr/local/boost-1.52.0/include", onto the front of each value in the ArrayList filenamesRelative.
Ideally, I would like to be able to do something like the following ;
filenameAbsolute = filenamesRelative.prefixAll(includeDir);
I don't necessarily have to use ArrayLists in the solution; I have just used them above for illustrative purposes.
From memory, you can do something like this rather easily in C++ using the STL, however my current working knowledge of Java isn't all that good unfortunately :(
Thanks in advance for any assistance.
I dont know of a method in the API. but its so simple just create your own. Something like:
List<String> filenameAbsolute = new ArrayList<String>();
for ( String file: filenamesRelative ) {
filenameAbsolute.add(prefix + file);
}
Do it like this:
ArrayList<String> prefixAll(ArrayList<String> filenamesRelative)
{
ArrayList<String> filenamesAbsolute = new ArrayList<String>();
String includeDir = "/usr/local/boost-1.52.0/include";
for ( String file: filenamesRelative ) {
filenameAbsolute.add(includeDir + file);
}//for
return filenameAbsolute;
}//prefixAll()

Categories

Resources