I have this task to mask the contents of a .CSV file using Java. Done with masking other field but the problem is masking the data in the Primary Key column.
I tried using the code below but it doesn't work. How should I do it?
String str = src.replaceAll("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "QLBNAVHTROFSEJMIKWPYGDUCZX");
There are a number of ways you could do this, but regular expressions are not the approach I would choose. I would build a map of character to character and then iterate the characters in a given string building the transposed output with the map (and don't forget digits and lowercase letters). Something like,
private static Map<Character, Character> MASK_MAP = new HashMap<>();
static {
String inChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
outChars = "QLBNAVHTROFSEJMIKWPYGDUCZX";
inChars += inChars.toLowerCase() + "0123456789";
outChars += outChars.toLowerCase() + "8652103749";
for (int i = 0; i < inChars.length(); i++) {
MASK_MAP.put(inChars.charAt(i), outChars.charAt(i));
}
}
private static String maskKey(String s) {
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
sb.append(MASK_MAP.get(s.charAt(i)));
}
return sb.toString();
}
And then call
String out = maskKey(inputString);
Related
I have to compare two string arrays. If the any of the characters in myArray match a character in argArray then I need to swap the case of the character in myArray. I'm almost there but am getting extra output.
This is what I have so far -
public class Main {
public static void main(String[] args) {
Main ob = new Main();
ob.reverse("bcdxyz#3210.");
}
public String reverse(String arg) {
String reverseCap = "";
String myStr = "abc, XYZ; 123.";
char[] argArray = arg.toCharArray();
char[] myArray = myStr.toCharArray();
for (int i =0; i < myArray.length; i++) {
for (int j =0; j < argArray.length; j++){
if (myArray[i] == argArray[j] && Character.isLowerCase(myArray[i])){
reverseCap += Character.toUpperCase(myArray[i]);
} else if (myArray[i] == argArray[j] && Character.isUpperCase(myArray[i])){
reverseCap += Character.toLowerCase(myArray[i]);
} else {
reverseCap += myArray[i];
}
}
}
System.out.println(reverseCap);
return null;
}
I want reverseCap to be "aBC, xyz, 123." but am getting the following -
"aaaaaaaaaaaaBbbbbbbbbbbbcCcccccccccc,,,,,,,,,,,, XXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZZZZZ;;;;;;;;;;;; 111111111111222222222222333333333333............
".
I've been staring at this for hours so I figured it was time to ask for help before I pluck my eyes out.
Marce noted the problem of adding characters to reverseCap on every iteration. Here is a solution that solves that problem and performs the case changes in place. Checking for a match first and then changing the case simplifies the logic a bit. Note myArray[i] needs to be lowercased before checking against arg[i] because the former may be an uppercase character; this is not needed for argArray[j] because those characters are assumed to be all lowercase. Finally, once the inner loop has matched, further iterations of it are no longer needed.
public class Main {
public static void main(String[] args) {
Main ob = new Main();
String testStr = "abc, XYZ; 123.";
String testArg = "bcdxyz#3210.";
System.out.println(testStr + " using " + testArg + " =>");
System.out.println(ob.reverse(testStr, testArg));
}
public String reverse(String myStr, String myArg) {
char[] myArray = myStr.toCharArray();
char[] argArray = myArg.toCharArray();
for (int i =0; i < myArray.length; i++) {
for (int j =0; j < argArray.length; j++) {
if (Character.toLowerCase(myArray[i]) == argArray[j]) {
if (Character.isLowerCase(myArray[i])) {
myArray[i] = Character.toUpperCase(myArray[i]);
} else if (Character.isUpperCase(myArray[i])) {
myArray[i] = Character.toLowerCase(myArray[i]);
}
break;
}
}
}
return String.valueOf(myArray);
}
}
With this part
} else {
reverseCap += myArray[i];
}
you're adding a character to reverseCap with every iteration, regardless if the characters match or not.
In your specific example, you could just leave that out, since every character in myStr also appears in arg, but if you want to add characters to reverseCap, even if they don't appear in arg, you'll need a way of checking if you already added a character to reverseCap.
Change
String reverseCap = "";
to
char[] reverseCap = new char[myStr.length()];
and then for each occurrence of
reverseCap +=
change that to read
reverseCap[i] =
Finally, convert reverseCap to a String:
String result = String.valueOf(reverseCap);
You are currently returning null. Consider returning result, and moving the System.out.println(...) into the main() method.
Update:
I think a better way to approach this is to use a lookup map containing upper/lower case pairs and their inverse to get the replacement character. The nested for loops are a bit gnarly.
/**
* Example: for the string "bcdxyz#3210."
* the lookup map is
* {B=b, b=B, C=c, c=C, D=d, d=D, X=x, x=X, Y=y, y=Y, Z=z, z=Z}
* <p>
* Using a map to get the inverse of a character is faster than repetitively
* looping through the string.
* </p>
* #param arg
* #return
*/
public String reverse2(String arg) {
Map<Character, Character> inverseLookup = createInverseLookupMap(arg);
String myStr = "abc, XYZ; 123.";
String result = myStr.chars()
.mapToObj(ch -> Character.toString(inverseLookup.getOrDefault(ch, (char) ch)))
.collect(Collectors.joining());
return result;
}
private Map<Character, Character> createInverseLookupMap(String arg) {
Map<Character, Character> lookupMap = arg.chars()
.filter(ch -> Character.isLetter(ch))
.mapToObj(this::getPairs)
.flatMap(List::stream)
.collect(Collectors.toMap(Pair::key, Pair::value));
System.out.println(lookupMap);
return lookupMap;
}
private List<Pair> getPairs(int ch) {
char upperVariant = (char) Character.toUpperCase(ch);
return List.of(
new Pair(upperVariant, Character.toLowerCase(upperVariant)),
new Pair(Character.toLowerCase(upperVariant), upperVariant));
}
static record Pair(Character key, Character value) {
}
But if one is not used to the Java streaming API, this might look a bit gnarly too.
Everywhere I look trying to find out how to convert a String to an int, the examples use a numeric string, i.e. they show how to change "123" into 123. That is not at all what I am trying to do. My string is alphabetic. I know this because the two previous methods required me, first, to check whether it contained uppercase characters, and then, convert it to all uppercase characters. I succeeded in doing both of these. Now the third method calls for converting it to an int, and performing an arithmetic function on it, and now I am stuck. I tried using .valueOf(), that is, the ascii numeric values of the characters, but it keeps throwing errors.
public class ChangeCase {
String stringToCheck;
public int convertToIntPlusTen() {
// Create new field for the converted string
String asciiValue = "";
// Break original string down to char array
final char[] chars = stringToCheck.toCharArray();
// Find the ascii value of each character and add it to the new field
for (int i = 0; i < chars.length; i++) {
asciiValue += String.valueOf((int) chars[i]);
}
// Convert string of numeric characters to int
int asciiInt = Integer.parseInt(asciiValue);
// Add ten to the resulting int
asciiInt += asciiInt + 10;
StringBuilder sbf
= new StringBuilder("");
sbf.append(asciiInt);
return asciiInt;
}
}
public class AppDriver {
public static void main(String[] args) {
ChangeCase changeCase = new ChangeCase();
changeCase.stringToCheck = "Foxes";
changeCase.convertToIntPlusTen();
}
}
Now since the ascii values of the characters are 'F' = 070, 'o' = 111, 'x' = 120, 'e' = 101, and 's' = 115, then I expected it to produce the numeric string "070111120101115," which would then become the int 070111120101115. Adding ten would make it 070111120101125, which is the expected output.
Instead I get:
Exception in thread "main" java.lang.NumberFormatException: For input string: "70111120101115"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:583)
at java.lang.Integer.parseInt(Integer.java:615)
at mainpackage.SubChangeCase.convertToIntPlusTen(SubChangeCase.java:45)
at mainpackage.AppDriver.main(AppDriver.java:133)
I'm thinking that maybe this is more of a logical error than an operational one, i.e. I may have approached the problem incorrectly in the first place. Because I see my stack trace does have the expected input string. Unfortunately, since almost every code example out there in internet world is about converting numeric strings, I have not found anything to help with this.
70111120101115 is too big for an integer. You have to store it in a long
You also made a typo - you instantiated the wrong class. It's ChangeCase, not SubChangeCase
Therefore, your class should be:
public long convertToIntPlusTen() {
// Create new field for the converted string
String asciiValue = "";
// Break original string down to char array
final char[] chars = stringToCheck.toCharArray();
// Find the ascii value of each character and add it to the new field
for (int i = 0; i < chars.length; i++) {
asciiValue += String.valueOf((int) chars[i]);
}
// Convert string of numeric characters to int
long asciiInt = Long.parseLong(asciiValue);
asciiInt += asciiInt + 10;
StringBuilder sbf
= new StringBuilder("");
sbf.append(asciiInt);
return asciiInt;
}
So your final code should be:
public class ChangeCase {
String stringToCheck;
public long convertToIntPlusTen() {
// Create new field for the converted string
String asciiValue = "";
// Break original string down to char array
final char[] chars = stringToCheck.toCharArray();
// Find the ascii value of each character and add it to the new field
for (int i = 0; i < chars.length; i++) {
asciiValue += String.valueOf((int) chars[i]);
}
// Convert string of numeric characters to int
long asciiInt = Long.parseLong(asciiValue);
asciiInt += asciiInt + 10;
StringBuilder sbf
= new StringBuilder("");
sbf.append(asciiInt);
return asciiInt;
}
}
public class AppDriver {
public static void main(String[] args) {
ChangeCase changeCase = new ChangeCase();
changeCase.stringToCheck = "Foxes";
changeCase.convertToIntPlusTen();
}
}
i have a problem with a algorithm.
I have a Map (Each key int its a hex unicode character) and a String with unicode characters.
I want to delete the next character in the string when i found a character that exists as key in my map.
for example my map contains those keys: 0x111,0x333,0x444,0x555,0x666 and my string its:
0x111+0xffff+0x444+0xEEEEE+0x666
I want to convert it to:
0x111+0x444+0x666
I have this but this doesnt work:
private String cleanFlags(String text) {
int textLong = text.length();
for (int i = 0; i < textLong; i++) {
if (flagCountryEmojis.containsKey(text.codePointAt(text.charAt(i)))) {
text = text.replace(text.substring(i + 1, i + 2), "");
textLong-=2;
}
}
return text;
}
How can i do it this?
Since you didn't mention anything about space complexity, I went ahead and took the liberties of using an array to solve the question:
public String cleanFlags(String text){
String [] arr = text.split("+");
String newText = "";
for(int i = 0; i < arr.length; i++){
if(flagCountryEmojis.containsKey(arr[i])){
newText += arr[i];
i++; // skips the next character
}
if(i < arr.length - 1)
newText += "+";
}
return newText;
}
Not sure if this solution solves your problem, since strings are immutable anyways, and calling "replace" simply creates a new string in the background, I went ahead and created a new string for you and returned the result when it is populated correctly.
Lemme know if there is something I am missing or other restrictions that were unmentioned.
I want to use hashtable to find unique characters as it seems more efficient to me so for example, hello in hashtable would be=> {h:1,e:1,l:2,o:1} & since value is more than 1 then string isnt unique. I know I can do ascii way of counting unique characters but I want to implement the hashtable way.
Please note, I do not want a regex implementation.
static void findHashUnique(String str)
{
Hashtable<Character, Integer> ht = new Hashtable<Character, Integer>();
for(int i=0;i<str.length();i++)
{
int cnt=1;
if(!ht.containsKey(str.charAt(i)))
{
ht.put(str.charAt(i), cnt);
}
}
System.out.print(ht);
}
I am stuck at the part of how will I first initialize the hashtable & also check if value exists then increment. In case of 'l' it will increment to 2 instead of being 1 since the key is the same.
Also Is this solution efficient?
Here's my approach.
String string = "hello";
Hashtable<Character, Integer> map = new Hashtable<>();
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (map.containsKey(c)) {
map.put(c, map.get(c) + 1);
} else {
map.put(c, 1);
}
}
System.out.println(map);
Output: {e=1, o=1, l=2, h=1}
Well, I don't know exactly what character encodings you will be examining, but if you are constraining yourself to only ASCII characters, you can use a simple array of 128 elements.
public static String uniqueLetters(String s) {
// storage for ascii characters
char[] letters = new char[128];
// mark counts of all letters
for(int i = 0; i < s.length(); i++) {
letters[ (int)s.charAt(i) ]++;
}
// find unique letters
String uniques = "";
for(int i = 0; i < letters.length; i++) {
if ( letters[i] == 1 ) {
uniques += Character.toString( (char)letters[i] );
}
}
return uniques;
}
To "fix" your code, use a HashSet<Character>, which uses a Hashtable internally, but you don't have to know or worry about that. Just keep adding the chars to the set and you'll be left with a unique set of chars at the end.
To achieve the intention more easily:
String uniqueChars = str.replaceAll("(.)(?=.*\\1)", "");
This is for the level system in a game.
The level consists of two byte arrays:
byte[] tiles and byte[] data
tiles holds the id of the tiles and data holds data.
I created a function to make a string out of them. It's formatted like tileId:tileData,tileId:tileData,tileId:tileData,etc
You can see an example of a complete level here: http://pastebin.com/X2LG7e80
The script looks like this:
public String toString() {
String s = "";
StringBuilder sb = new StringBuilder();
for (int t = 0; t < tiles.length; t++) {
sb.append(tiles[t]).append(":").append(data[t]).append(t == tiles.length - 1 ? ";" : ",");
}
s = sb.toString();
return s;
}
Now I need a way to turn it back into two byte arrays.
I tried a couple of different things but none of them worked.
Assuming a variable stringRep contains the string representation:
String stringRep = "tileId:tileData,tileId:tileData,tileId:tileData";
String[] pairs = stringRep.split(",");
byte[] tiles = new byte[pairs.length];
byte[] data = new byte[pairs.length];
int i = 0;
for(String pair : pairs){
String[] pairParts = pair.split(":");
titles[i] = Byte.parseByte(pairParts[0]);
data[i] = Byte.parseByte(pairParts[1]);
i++;
}