1

I was wondering how i can get these values and put it together in a new String

class Rna {


    static Map<Character, Character> map = (Map<Character, Character>) Map.of(
            'G', 'C',
            'C', 'G',
            'T', 'A',
            'A', 'U'
    );

    String transcribe(String dnaStrand) {
        return dnaStrand.chars()
                .map(c -> map.get(c))
                .collect(StringBuilder::new, StringBuilder::appendCodePoint ,StringBuilder::append)
                .toString();
    }
}

transcribe should return an String like "AUGC". Now i`m getting this exception. enter image description here

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
mark882
  • 25
  • 5
  • Hello and welcome! Please include what you've tried, what results you are getting and examples of what you are expecting. I suggest [writing a unit test](https://junit.org/junit5/docs/current/user-guide/#overview) as its results will help you while you experiment with the `transcribe` method. – pyb Jan 27 '22 at 18:59
  • 5
    Please replace screenshot with text; images are not searchable. – Abhijit Sarkar Jan 27 '22 at 19:47
  • 2
    That screen shot is very hard to read. Please paste the entire stack trace as text instead. – VGR Jan 27 '22 at 20:50
  • 1
    Downvoting because the exception is provided as a screenshot instead of as text. – Joundill Jan 28 '22 at 03:53

5 Answers5

3

I think the problem is that you use the String.chars() method which returns a stream of ints but you cannot expect autoboxing of an int to a Character.

The map doesn't contain Integer keys, so always will return null for the get.

As a solution you could try to cast c to a char or even create a Character instance manually:

.map(c -> map.get((char)c)) or .map(c -> map.get(Character.valueof((char)c))

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
cyberbrain
  • 3,433
  • 1
  • 12
  • 22
3

char obsolete

The char primitive type, and its wrapper class Character, have been legacy since Java 2. As a 16-bit value, these types are physically incapable of representing most characters.

Avoid char/Character types.

Code points

Instead, use Unicode code point integer numbers to work with individual characters.

Your code is mixing char values with code point code. Do not mix the types. Avoid char, use only code points.

Unfortunately, Java lacks a type for code points. So we use the int/Integer with several methods awkwardly spread out across various classes including String, StringBuilder, and Character.

Map< Integer , Integer > transMap = 
    Map.of(
        "G".codePointAt(0) , "C".codePointAt(0) ,
        "C".codePointAt(0) , "G".codePointAt(0) ,
        "T".codePointAt(0) , "A".codePointAt(0) ,
        "A".codePointAt(0) , "U".codePointAt(0) 
    )
;

The transcription method swaps integers. Then collect these integers using StringBuilder#appendCodePoints.

By the way, your transcribe method could be made more general by taking an parameter of type CharSequence rather than String. CharSequence is an interface implemented by String. The only method we need to call on the argument is codePoints. That method is required by CharSequence.

String transcribe( final CharSequence dnaStrand ) {
    return 
        dnaStrand
        .codePoints()
        .map( codePoint -> transMap.get( codePoint ) )
        .collect(
            StringBuilder :: new, 
            StringBuilder :: appendCodePoint, 
            StringBuilder :: append 
        )
        .toString() ;
}
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Thanks for pointing out the `char`/`Character` limitations. In this very specific question, transcribing DNA to RNA can be done using ASCII only: they're made of 4-5 nucleotides, each encoded to a latin letter from the question's `map`. – pyb Jan 28 '22 at 04:34
  • 2
    @pyb While you are correct technically in the specific case of these five characters, there is **no benefit to using `char`** rather than code points. By making a habit of writing code for code points, you’ll avoid having obscure failures at runtime when your app encounters a character outside the [Unicode basic plane](https://en.m.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane). – Basil Bourque Jan 28 '22 at 04:48
  • 1
    I agree, the operation should be written to consistently use codepoints. It is already using `StringBuilder::appendCodePoint` for simplicity. Using a `Map` also allows to write `.map(transMap::get)`, as no conversions happen anymore. So, also using `dnaStrand.codePoints()` is consistent. But I wouldn’t write the constants, which are clearly ASCII letters, in such a verbose way. Even `Map transMap = Map.of((int)'G', (int)'C', (int)'C', (int)'G', (int)'T', (int)'A', (int)'A', (int)'U');` would be simpler. – Holger Feb 01 '22 at 17:10
  • 1
    Further note that the first Java version allowing to assume that `char` is legacy, is Java 5. Earlier versions lack the APIs to deal with codepoints. And perhaps, `static final int A = 'A', C = 'C', G = 'G', T = 'T', U = 'U'; static final Map DNA_RNA = Map.of(G,C, C,G, T,A, A,U);` is even more readable than the variant of my previous comments. – Holger Feb 01 '22 at 17:15
2

Here is one way to do it. The method is called with a string of keys from your base/pair map. Any characters in that string map to null are filtered out. This presumes a map instance visible to the method. Otherwise it will need to be provided as an argument.

  • chars() provides a stream of characters
  • get the mapping for current character and convert to String
  • filter out any null values.
  • join String characters into long strand and return.
static String transcribe(String dnaStrand) {
    return dnaStrand.chars()
            .mapToObj(c -> String.valueOf(map.get((char)c)))
            .filter(str->!str.equals("null"))
            .collect(Collectors.joining());
}

System.out.println(transcribe("GCTA"));

will return and print

CGAU
WJS
  • 36,363
  • 4
  • 24
  • 39
2

Method get() of the Map interface accepts an argument of Object type as a parameter. Hence you can pass to it an instance of pretty much any type. Not necessarily of the type that matches the type the key. In such a case map will return null.

And that's basically what you are doing. Method chars() of the String class returns IntStream a stream primitive int values (note there is NO such thing as CharStream in java). Attempt to get a value from a Map<Character, Character> result in null because the map will look for the object of type Integer, and obviously, there will be no match.

Instead, you have to cast int to char explicitly and it'll be autoboxed into Character wrapper type.

Also, note that for any given string that contains any other character apart from uppercase 'G', 'C', 'T', 'A' your code will fail with NullPointerException.

If a fail-fast implementation wasn't your intent as a workaround you can use map.getOrDefault() instead of map.get().

Method getOrDefault() returns a default value instead of null for any key that isn't represented in the map.

For instance, using getOrDefault() you can preserve only characters that are contained in the map and eliminate all others by providing a default value that will be removed from the final string (like that getOrDefault((char) c, '!')), and then apply replace("!", "") to the result).

Another possible option that can be achieved with getOrDefault()is to change only those characters in the string that are contained in the map whilst others will remain intact as it's shown in the example below.

    public String transcribe(String dnaStrand) {
        return dnaStrand.chars()
                .map(c -> map.getOrDefault((char) c, (char) c))
                .collect(StringBuilder::new, 
                        StringBuilder::appendCodePoint, 
                        StringBuilder::append)
                .toString();
    }
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • 1
    I don’t think that using `getOrDefault`, to silently getting wrong results in the case of errors, is an improvement over getting exception in the case of errors. – Holger Feb 01 '22 at 17:18
  • @Holger I agree this advice isn't applicable to a practical scenario. As the code snippet provided by PO rather seems to be created for learning purposes my intention was to introduce possible options on how to play around with this code. I've changed my comment to make it more clear. – Alexander Ivanchenko Feb 02 '22 at 14:48
-1

If you want to obtain map value, you can not have a String like "AUGC", may be "UAGC"? And you can read more about Collection, Map and LinkedHashMap. If you will try this code, all will be work.

public class Main {
    public static void main(String[] args) {


        Map<Character, Character> map = new LinkedHashMap();
            map.put('G', 'C');
        map.put( 'C', 'G');
                map.put( 'T', 'A');
                        map.put( 'A', 'U');


Test test = new Test();
        System.out.println(test.transcribe(map));
    }
}
public class Test {

    String transcribe(Map<Character, Character> map) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Character ch : map.values()) {
            stringBuilder.append(ch);
        }
        return String.valueOf(stringBuilder.reverse());
    }
}
  • From the name of the classes and method, and the `transcribe` implementation, it seems the question is about translating (mapping) each character of a string one by one. So that `ATCG` gives `UAGC`. – pyb Jan 28 '22 at 04:17