Find the index value of a character stored in HashMap - java

I'm looking out to print the index value of a respective character as many times as it would appear using HashMap.
For example, let's say I have String str = "Hello World". The program as of now displays the occurence of characters via {d=1, W=1, e=1, r=1, o=2, l=3, H=1}.
What I intend to achieve as resultset is {d=[9], o=[4, 6], r=[7], W=[5], H=[0], l=[2, 3, 8], e=[1]} where *=[*] represents key=[indexValue].
(The whitespace character is not to be taken into consideration in the final resultSet.)
import java.util.HashMap;
import java.util.Map;
public class ConcordanceOfStrings {
public static void main(String[] args) {
String str = "Hello World";
//code to remove whitespaces
String newStr = str.replaceAll(" ", "");
Map<Character, Integer> numCount = new HashMap<Character, Integer>(Math.min(newStr.length(), 26));
System.out.println("The count is: ");
for(int i=0; i<newStr.length(); i++){
char charAt = newStr.charAt(i);
if(!numCount.containsKey(charAt)){
numCount.put(charAt, 1);
}
else{
numCount.put(charAt, numCount.get(charAt)+1);
}
}
System.out.println(numCount);
}
}

You're very close. Right now, you are storing the result in a Map<Character, Integer>, so a map from each character to the number of times it appears in the String.
To store all the indexes where the character appears, you need to have a Map<Character, List<Integer>>: each character will be mapped to a list of integers, which will be the list of the indexes where this character appears.
In your current code, you just need to adapt the logic that populates the map:
if(!numCount.containsKey(charAt)){
numCount.put(charAt, new ArrayList<>(Arrays.asList(i))); // <-- we store a list containing the first index i
// numCount.put(charAt, 1);
} else{
numCount.get(charAt).add(i); // <-- we add to the existing list the index i
// numCount.put(charAt, numCount.get(charAt)+1);
}
In the case where the map doesn't contain the character, we initialize the mapping with a list containing the first index i. Arrays.asList(i) returns a fixed-size list so I wrapped it in another ArrayList.
In the case where the map already contains the character, we just need to get the current list of indexes and add the one we just found.
If you are using Java 8, your whole code could be written more simply with Streams:
Map<Character, List<Integer>> numCount =
IntStream.range(0, str.length())
.filter(i -> str.charAt(i) != ' ')
.boxed()
.collect(Collectors.groupingBy(
i -> str.charAt(i),
Collectors.mapping(v -> v - 1, Collectors.toList())
));

You need something more than just Map you need something like Map>. Here is a example of a modification to your algorithm:
Map<Character, List<Integer>> positions = new HashMap<>();
Map<Character, Integer> numCount = new HashMap<Character, Integer>(Math.min(newStr.length(), 26));
for (int i = 0; i < newStr.length(); i++) {
char charAt = newStr.charAt(i);
if (!numCount.containsKey(charAt)) {
numCount.put(charAt, 1);
}
else {
numCount.put(charAt, numCount.get(charAt) + 1);
}
if(!positions.containsKey(charAt)){
List<Integer> cPosition = new LinkedList<>();
cPosition.add(i);
positions.put(charAt, cPosition);
}
else{
List<Integer> cPosition = positions.get(charAt);
cPosition.add(i);
//positions.put(charAt, cPosition); because of references there is no need to do this
}
}

Related

How do you check if a word has an anagram that is a palindrome?

How do you compare a palindromic word to one of the newly formed words of an anagram?
And how do you grab one of the newly formed words for it to be compared to the input word?
This is my code:
public class SampleCode2 {
public static boolean isPalindromic(String word, String mark) {
if (word.length() == 0) {
}
for (int i = 0; i < word.length(); i++) {
String newMark = mark + word.charAt(i);
String newLetters = word.substring(0, i) +
word.substring(i + 1);
}
String ifPalindrome = ""; //will store here the reversed string
String original = word; //will store here the original input word
//to reverse the string
for (int i = word.length() - 1; i >= 0; i--) {
ifPalindrome += word.charAt(i);
}
//to compare the reversed string to the anagram
if (word.equals(ifPalindrome)) {
return true;
} else {
return false;
}
}
public static void main(String[] args) {
boolean check = isPalindromic("mmaad", "");
System.out.println(check);
}
}
It's not yet done because the permutation and comparison won't work. The output is displaying false, I need it to be true because the anagram of MMAAD is madam. And I have to check if madam is indeed a palindrome of mmaad.
So What I did is used HashMap instead of creating words from the given word
A String can be of even or odd length
If "EVEN" String is palindrome then every "character" in the String will appear even times eg: String str = maam : m=2, a=2
If "ODD" String is a palindrome then there will only be 1 character of odd occurrence and the rest will occur even times.
eg: String str = mmaad: m=2,a=2,d=1
To store the Occurrence of the Characters in the String we will use HashMap where Character of the String is the KEY and its occurrence is VALUE
HashMap<Character,Integer> mapChar = new HashMap<>();
And we will add each Character in the HashMap with the number of times it has appeared in the String.
Now we will check if the String length is "even" or "odd"
if "EVEN" Length String we know that every character will appear EVEN times and if any time a character appears "ODD" times we return false i.e It's not a Palindrome
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 != 0) {
return false;
}
}
If "ODD" Length String we know that only one Character will appear odd time and the rest will be of EVEN Occurrence
And if there are 2 characters that occur odd times then its not a palindrome
// Number of times odd value Character as occurred
int occur1 = 0;
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 == 1) {
occur1++;
if (occur1 > 1) {
return false;
}
}
}
Here's the whole Code:
public static void main(String[] args) throws Exception {
boolean check = isPalindromic("malayalam", "");
System.out.println(check);
}
public static boolean isPalindromic(String word, String mark) {
boolean isPal = true;
if (word.length() == 0) {
return false;
}
HashMap<Character, Integer> mapChar = new HashMap<>();
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if (mapChar.containsKey(ch)) {
mapChar.put(ch, mapChar.get(ch) + 1);
} else {
mapChar.put(ch, 1);
}
}
if (word.length() % 2 == 0) {
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 != 0) {
return false;
}
}
} else {
int occur1 = 0;
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 == 1) {
occur1++;
if (occur1 > 1) {
isPal = false;
break;
}
}
}
}
return isPal;
}
output:
mmaa
Is Palindrome: true
mmaad
Is Palindrome: true
niti
Is Palindrome: false
If you want to find a list of anagrams of a word that are palindromes, you can split this task into two parts: first get a list of the character permutations of that word List<String>, i.e. a list of anagrams, and then filter those strings that are palindromes. For example, for the mmaad string, the palindromes are:
madam
amdma
Getting a list of permutations is an expensive operation for large strings, since the number of permutations is a factorial of the string length. For example, for the mmaad string, there are 120 permutations. Filtering palindromes after that is cheaper.
Try it online!
public static void main(String[] args) {
// list of distinct permutations
getPermutations("mmaad")
// Stream<String>
.stream()
// filter palindromes
.filter(str -> isPalindrome(str))
// output
.forEach(System.out::println);
}
/**
* #param str source string, may contain surrogate pairs.
* #return whether the source string is a palindrome.
*/
public static boolean isPalindrome(String str) {
// array of characters of the string
int[] chars = str.codePoints().toArray();
return IntStream
// iterate from the beginning to the middle of the string
.range(0, chars.length / 2)
// compare the characters: first - last, second - penultimate
.allMatch(i -> chars[i] == chars[chars.length - i - 1]);
}
/**
* #param str source string, may contain surrogate pairs.
* #return a list of distinct permutations of characters of the source string.
*/
public static List<String> getPermutations(String str) {
// array of characters of the string
int[] chars = str.codePoints().toArray();
return IntStream.range(0, chars.length)
// Stream<List<Map<Integer,String>>>
.mapToObj(i -> IntStream.range(0, chars.length)
// represent each character as Map<Integer,String>
.mapToObj(j -> Map.of(j, Character.toString(chars[j])))
// collect a list of maps
.collect(Collectors.toList()))
// reduce a stream of lists to a single list
.reduce((list1, list2) -> list1.stream()
// summation of pairs of elements,
// i.e. maps, from two lists
.flatMap(map1 -> list2.stream()
// filter out those keys
// that are already present
.filter(map2 -> map2.keySet().stream()
.noneMatch(map1::containsKey))
// join entries of two maps
.map(map2 -> {
Map<Integer, String> map =
new LinkedHashMap<>();
map.putAll(map1);
map.putAll(map2);
return map;
}))
// collect into a single list
.collect(Collectors.toList()))
// List<Map<Integer,String>>
.orElse(List.of(Map.of(0, str)))
// Stream<Map<Integer,String>>
.stream()
// map of strings to a single string
.map(map -> String.join("", map.values()))
.distinct()
// list of distinct permutations
.collect(Collectors.toList());
}
See also:
• How to create all permutations of tuples without mixing them?
• Reverse string printing method

How do I be inclusive of Letters into?

String
input="4,8,222cd,77,77A";
I sorted the values.for example, I sorted 3,4,5,6 consecutive values in the range [3-6].I have done consecutive numbers "[]" in this format but I couldn't make the letters. Sorted list should not be disrupted.
Here is an example method to merge the letters at the end:
static List<String> mergeLetters(List<String> items){
Map<String, List<String>> mergingCandidates = new HashMap<>();
for (String s : items) {
int length = s.length();
if (length > 1 && Character.isLetter(s.charAt(length - 1))) {
String key = s.substring(0, length - 1);
String value = s.substring(length - 1);
List<String> tmp = mergingCandidates.computeIfAbsent(key, k -> new LinkedList<String>());
tmp.add(value);
}
}
for (Map.Entry<String, List<String>> entry : mergingCandidates.entrySet()) {
String key = entry.getKey();
List<String> characterList = entry.getValue();
if (characterList.size() > 1) {
boolean consecutive = true;
Character[] characters = characterList.stream().map(s -> s.charAt(0)).toArray(Character[]::new);
for (int i = 0; i < characters.length - 1; i++) {
if (characters[i + 1] - characters[i] != 1) {
consecutive = false;
break;
}
}
if (consecutive) {
int indexToBeReplaced = items.indexOf(key + characterList.get(0));
String newValue = key + "[" + characterList.get(0)
+ "-" + characterList.get(characterList.size() - 1) + "]";
characterList.forEach(letter -> items.remove(key + letter));
items.add(indexToBeReplaced, newValue);
}
}
}
return items;
}
Explanation:
You find all the sequences that differ only on the last letter and make a Map out of them.
You pick the Map entries that have 2 or more ending letters.
You check if the letters are consecutive.
If yes, remove them and add a new merged entry instead. Be careful to add the new entry at the same index.
The main method should sort the items, first merge the digits as you correctly do now, and then replace the letters that can be merged:
String input = "605,2A,401-2A,32C,21F,201A,605A,401-1A,200-2E,462A,462,583-58D,200,462-1A,583/58E,583-57D, 542,2B, 1, 542/2E, 801, 802, 803, 605B, 32D, 3, 603, 4, 6, 5, 60, 201C, 542/2D,40,20,2C,800,800-1,50,200-2C,21C,800A,200A,571-573L,51/2,470/1,51/1,571-573K,454-1,444-446,571-573M";
List<String> items = Arrays.asList(input.split("\\s*,\\s*"));
items.sort(Comparator.naturalOrder());
items.sort(Comparator.comparingInt(o -> Integer.parseInt(o.split("\\D")[0])));
List<String> merged = mergeRanges(items);
List<String> mergeLetters = mergeLetters(merged);
System.out.println(("Result: " + mergeLetters));
The output for your input is:
Result: [1, 2[A-C], [3-6], 20, 21C, 21F, 32[C-D], 40, 50, 51/1, 51/2, 60, 200, 200-2C, 200-2E, 200A, 201A, 201C, 401-1A, 401-2A, 444-446, 454-1, 462, 462-1A, 462A, 470/1, 542, 542/2[D-E], 571-573[K-M], 583-57D, 583-58D, 583/58E, 603, 605, 605[A-B], 800, 800-1, 800A, [801-803]]
The general idea is first to expand all the declarations in the input, then sort them, then to emit them again, while compacting ranges when you encounter successive items,.
Expand all declarations: 571-573K (if I understand correctly) would expand to the three items 571K, 572K, 573K. Most simple declarations just expand to themselves.
Sort: You need a sort function that is sensitive to both numbers and letters. It will probably have to break up the input string and compre the number part numerically, but the letter part alphabetically.
Emit: Walk the sorted list and emit each item; however, if the next item is adjacent to the current item, merge it with the current item instead. This will probably require that you create a temporary list of outputs, and delay the actual output until you've gone through the original list.

String Occurrence According to the order

I have a Problem where i need to find the first occurrence of the char according to the order which they occur in the given String.
For Example :
I have a String "Unitedin" where the char "n" and "i" have a multiple occurrence in the String.
char n occurred at charAt(1,7)
char i occurred at charAt (2,6)
But the char "i" had occurred first before char "n".
I have Tried Something like this But i am not getting the required output.Can Anyone Help me please?
Note : Challenge is Not to use any type of List or Hashset or Hashmap
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
StringBuilder nodup = new StringBuilder();
StringBuilder dup = new StringBuilder();
System.out.println("Enter a string : ");
String instring = in.next();
for (int i = 0; i < instring.length(); i++) {
for (int j = i + 1; j < instring.length(); j++) {
if (instring.charAt(i) == instring.charAt(j)) {
nodup.append(instring.charAt(i));
} else {
dup.append(instring.charAt(i));
}
}
}
System.out.print(nodup.toString());
i am getting the OutPut as : ni . but the Required output is in.
The LinkedHashMap class works really well for your particular problem. You may iterate the character array from your input string, and then add the characters, along with their counts, as keys, into the LinkedHashMap. Since LinkedHashMap preserves insertion order, to get the output you want you need only iterate the map and print out all keys whose counts are greater than one. The order of the character keys will be fixed at the first occurrence of each letter.
String input = "Unitedin";
LinkedHashMap<Character, Integer> map = new LinkedHashMap<>();
for (char chr : input.toCharArray()) {
Integer count = map.get(chr);
map.put(chr, count == null ? 1 : count + 1);
}
for (Map.Entry<Character, Integer> entry : map.entrySet()) {
if (entry.getValue() > 1) System.out.println(entry.getKey());
}
This prints:
n
i
If your objective is to view the first recurrence of a character, then this should do it. The order will be the order in which a repeated character is found in your string.
String input = "Unitedin";
Stringbuilder sb = new Stringbuilder();
Map<Character, Integer> map = new HashMap<>();
for (char chr : input.toCharArray()) {
Integer count = map.get(chr);
count = count != null ? count + 1 else 1;
map.put(chr, count);
if(count > 1) {
sb.append(chr);
}
}
System.out.println(sb.toString()); // Output: in
EDIT: I understand from your comment that you cannot use an instance of List, HashSet, or HashMap? Then is LinkedHashMap allowed? If not, then you'll need to recreate that functionality somehow.
You could map characters a-z and A-Z in an int array of 52 (26 + 26). Rather than insert the count into a map, you map the character to its appropriate spot in the array and update the count. Good luck!

Java count string index 0 in array

Im trying to keep track of occurrences of strings that start with certain letters in an array. Given the following array, count the number of occurrences of strings that start with "T", "B, and "A" for example..
String[] month_codes = {"July 2007", "TRUCKS", "APPLES", "BEANS", "COATS", "BANANA", "CODES"}
Output would look like this for each month:
T A B
July 2 1 2
Augu ....
Sept ....
This is the code I used but it only captures one occurrence
public static int extract_counts(String[] month_array){
for(int i=0; i < month_array.length; i++){
System.out.println(month_array[i]);
if(month_array[i].startsWith("T")){
july_ab++;
}
}
return july_ab;
}
What I would do is iterate through all of the items once, counting the number of occurrences. Then I'd print the results.
The easiest way to do this, in my opinion, would be to use an array of integers representing the 26 letters in the English alphabet:
int[] frequency = new int[26];
// frequency[0] is the number of words that start with A
Here, we can take advantage of the fact that Java initializes an integer arrays with all zeroes.
Then we'd iterate through the month_array and count how many words start with each letter.
for( String word : month_array ) {
char firstLetter = word.toLowerCase().charAt(0);
int index = firstLetter - 'a'; // a = 0, b = 1, etc.
frequency[index]++;
}
At the end of this for-loop, the frequency array now contains how many words start with each letter. We could print them like this:
for(int i = 0; i < 26; i++) {
System.out.print( frequency[i] );
}
Of course, this would be in order from A to Z. You'd also probably want to print the headers with the letters A-Z first before iterating through your months.
In your example, your first word is "July 2007" -- of course you'd need to omit this first item before counting since you'd otherwise be artificially incrementing (for example) the 'J' counter.
public int[] extractCounts( String[] month_array ) {
int[] frequency = new int[26];
for(int i = 1; i < month_array.length; i++) {
// here, we start at 1 to omit the 'July 2007' record
String word = month_array[i];
char firstLetter = word.toLowerCase().charAt(0);
int index = firstLetter - 'a';
frequency[index]++;
}
return frequency;
}
public void printCounts(String month, int[] frequencies) {
System.out.print(month + "\t");
for(int frequency : frequencies) {
System.out.print(frequency);
System.out.print("\t");
}
}
String[] month_codes = ...;
Map<Character, Long> result = Arrays.stream(month_codes)
.collect(Collectors.groupingBy(
str -> Character.toLowerCase(str.charAt(0)),
Collectors.counting()));
So, Map<Character, Long> contains following data:
'a' -> 1
'b' -> 2
'c' -> 2
't' -> 1
'j' -> 1
You can do it in several ways.
I am providing you a way which can find the occurrences (Starting chars) using single loop and Map to store only occurrence of String start with "A", "B" and "T".
Here is the code:
public static Map<String, Integer> extract_counts(String[] month_array){
Map<String, Integer> map = new HashMap<>();
for(int i=0; i < month_array.length; i++){
//System.out.println(month_array[i]);
if(month_array[i].startsWith("T")){
if(map.containsKey("T")){
map.put("T", map.get("T") + 1);
}else {
map.put("T", 1);
}
}
if(month_array[i].startsWith("A")){
if(map.containsKey("A")){
map.put("A", map.get("A") + 1);
}else {
map.put("A", 1);
}
}
if(month_array[i].startsWith("B")){
if(map.containsKey("B")){
map.put("B", map.get("B") + 1);
}else {
map.put("B", 1);
}
}
}
return map;
Calling the method:
String[] month_codes = {"July 2007", "TRUCKS", "APPLES", "BEANS", "COATS", "BANANA", "CODES"};
System.out.println(extract_counts(month_codes));
Will get output like this:
{A=1, B=2, T=1}

Counting letter frequencies and print letter:count, but not duplicates in java strings

public static void main(String[] args) {
TextIO.putln("Text?");
String line = TextIO.getln();
line = line.toLowerCase();
char[] arr = line.toCharArray();
for(int i = 0; i < line.length(); i++){
if(Character.isLetter(arr[i])){
int count;
int j;
for(j = 0,count = 0; j < line.length(); j++){;
if( arr[i] == arr[j]){
count++;
}
}
System.out.printf("%c:%d\n", arr[i],count);
}
}
}
If I enter the string josh it prints out
j:1
o:1
s:1
h:1
If I enter joshh it prints
j:1
o:1
s:1
h:2
h:2
but I want
j:1
o:1
s:1
h:2
How do I, for any string with duplicates, only print out the unique letter and how many times it occurs total? I was thinking of maybe implementing a for loop that just checks for each letter and increment it such as
for ('a' : string)
total++
and then increment the 'a' by one, so the next loop would check how many occurrences of b, then c, and so on.
How much java do you know? Is this an assignment? Have you been through data structures? Two options:
Sort and then count
Use a map (dictionary if you come from Python as your username suggests), increment in the first pass and then iterate over the keys to print the values
From what I can tell, it looks like the issue lies in the for loop you are using while printing.
What happens is that you evaluate for every single character in the string and print. Once you evaluate for the first h in josh, your program then moves on to the second h, evaluates again, and as a result, prints h and its count again.
You could try a data structure such as a dictionary which accounts for unique elements. Or, as you count how many times letters appear, you can have another array that holds letters already seen. If you check against this array before counting how many times a letter appears you can see if the letter has already been counted, if so you can just skip this character and move on to the next. It isnt the most elegant solution, but it would work
public static void main(String[] args) {
TextIO.putln("Text?");
String line = TextIO.getln();
line = line.toLowerCase();
// char[] arr = line.toCharArray();
Map<Character, Integer> occurrences=new LinkedHashMap<Character, Integer>();
for(int i = 0; i < line.length(); i++){
int ch=line.charAt(i);
if(Character.isLetter(ch)){
if(occurrences.containsKey(ch)) {
occurrences.put(ch, occurrences.get(ch)+1); // increment
}
else {
occurrences.put(ch, 1);
}
}
}
for(char ch : occurrences.keySet()) {
System.out.print(ch+":"+occurrences.get(ch)+";");
}
System.out.println();
}
Try this.
Map<Character, Integer> map = new LinkedHashMap<>();
for (char c : line.toCharArray())
if (Character.isLetter(c))
map.compute(c, (k, v) -> v == null ? 1 : v + 1);
for (Entry<Character, Integer> e : map.entrySet())
System.out.printf("%c:%d\n", e.getKey(), e.getValue());

Categories

Resources