How to convert a Navigablemap to String[][] - java

I need to convert a navigable map to a 2d String array.Below given is a code from an answer to one of my previous question.
NavigableMap<Integer,String> map =
new TreeMap<Integer, String>();
map.put(0, "Kid");
map.put(11, "Teens");
map.put(20, "Twenties");
map.put(30, "Thirties");
map.put(40, "Forties");
map.put(50, "Senior");
map.put(100, "OMG OMG OMG!");
System.out.println(map.get(map.floorKey(13))); // Teens
System.out.println(map.get(map.floorKey(29))); // Twenties
System.out.println(map.get(map.floorKey(30))); // Thirties
System.out.println(map.floorEntry(42).getValue()); // Forties
System.out.println(map.get(map.floorKey(666))); // OMG OMG OMG!
I have to convert this map to a 2d String array:
{
{"0-11","Kids"},
{"11-20","Teens"},
{"20-30","Twenties"}
...
}
Is there a fast and elegant way to do this?

Best bet is just to iterate through the Map and create an array for each entry, the troublesome part is generating things like "0-11" since this requires looking for the next highest key...but since the Map is sorted (because you're using a TreeMap) it's no big deal.
String[][] strArr = new String[map.size()][2];
int i = 0;
for(Entry<Integer, String> entry : map.entrySet()){
// current key
Integer key = entry.getKey();
// next key, or null if there isn't one
Integer nextKey = map.higherKey(key);
// you might want to define some behavior for when nextKey is null
// build the "0-11" part (column 0)
strArr[i][0] = key + "-" + nextKey;
// add the "Teens" part (this is just the value from the Map Entry)
strArr[i][1] = entry.getValue();
// increment i for the next row in strArr
i++;
}

you can create two Arrays, one with the keys and one with the values in an "elegant way" then you can construct an String[][] using this two arrays.
// Create an array containing the values in a map
Integer[] arrayKeys = (Integer[])map.keySet().toArray( new Integer[map.keySet().size()]);
// Create an array containing the values in a map
String[] arrayValues = (String[])map.values().toArray( new String[map.values().size()]);
String[][] stringArray = new String[arrayKeys.length][2];
for (int i=0; i < arrayValues.length; i++)
{
stringArray[i][0] = arrayKeys[i].toString() + (i+1 < arrayValues.length ? " - " + arrayKeys[i+1] : "");
stringArray[i][1] = arrayValues[i];
}

Related

JAVA Iterateing through Hasmap with list

I need iterate through hashmap and get key value which should be a string and all values within that key which is a list of strings that have strings?
Psuedo code
static HashMap<String, List<String>> vertices = new HashMap<String, List<String>>();
for (int i = 0; i < vertices.size(); i++)
{
String key = vertices.getKey at first postions;
for (int x = 0; x < size of sublist of the particular key; x++)
{
String value = vertices key sublist.get value of sublist at (i);
}
}
You can't iterate over HashMap directly, as there is no numeric index of values within HashMap. Instead key values are used, in your case of type String. Therefore the values don't have a particular order. However, if you want, you can construct a set of entries and iterate over that, using vertices.entrySet().
for (Entry<String, List<String>> item : vertices.entrySet()) {
System.out.println("Vertex: " + item);
for (String subitem : item.getValue()) {
System.out.println(subitem);
}
}
Try vertices.keySet();
It gives a Set of all keys in the map. Use it in a for loop like below
for (String key : vertices.keySet()) {
for (String value : vertices.get(key)) {
//do stuff
}
}

Sort an 2D array by number of hits

I have two ArrayLists:
Array 01:
ArrayList<String> uniqueFiletypes --> which contains unique filetypes (e.g .zip etc..)
Array 02:
ArrayList<Integer> countFiletypes --> which counts how many of each filetype there is, for example 8 .zip's
And to skip right to the question:
I need to make some kind of "ranking", which means the highest count of filetypes gets the first place, etc...
Another problem: It must be an Object[][] (to support JTable), so it is possible to show the result easily.
Example of output: I have 8 .zips, 5 .java and 2 .docx
Object[][] = {{"1", ".zip", "8"},{"2", ".java", "5"}, {"3", ".docx", "2"}}
Where {PLACE, FILETYPE, COUNT}
I'm assuming the order of the items in both lists matches. I.e. the first item in the uniqueFiletypes list has the number of hits equal to the first number in the countFiletypes list.
I would do the following:
Loop through the lists, adding the entries to a Map.
Sort the list of countFiletypes in descending order.
Pull the file types from the map, adding them in the order they're now in in the ordered list.
Something like the following might do the trick:
public static void main(final String[] args) {
final ArrayList<String> uniqueFileTypes = new ArrayList<String>();
uniqueFileTypes.add(".java");
uniqueFileTypes.add(".zip");
uniqueFileTypes.add(".docx");
final ArrayList<Integer> countFileTypes = new ArrayList<Integer>();
countFileTypes.add(5);
countFileTypes.add(8);
countFileTypes.add(2);
final Map<Integer, String> countedFileTypes = new HashMap<Integer, String>();
for (int i = 0; i< uniqueFileTypes.size(); i++ ) {
countedFileTypes.put(countFileTypes.get(i), uniqueFileTypes.get(i) );
}
Collections.sort(countFileTypes);
Collections.reverse(countFileTypes);
final Object[][] data = new Object[countedFileTypes.size()][3];
for(int i = 0; i<countedFileTypes.size(); i++) {
final Integer count = countFileTypes.get(i);
data[i] = new Object[]{(i+1), countedFileTypes.get(count), count};
System.out.println("{" + (i+1) + "," + countedFileTypes.get(count) + "," + count + "}");
}
}
The main method and the system out aren't really needed, I just used them for testing my solution, which produced this output:
{1,.zip,8}
{2,.java,5}
{3,.docx,2}
Granted, this implies a link between the number of hits and the file type which may not be true. For example, if the docx and the java file format both have 9 hits, this solution wouldn't work.
Is it possible for you to merge the two ArrayLists to a single HashMap<String, Integer>?
This map can hold the entries consisting of the unique filetype (String) and its count (Integer). I suggest this because you have a direct link between a filetype and its count - this "link" can be expressed by a HashMap entry.
The conversion of the HashMap to an Object[][] can be done this way:
for (Map.Entry<?,?> entry : map.entrySet()) {
model.addRow(new Object[] { entry.getKey(), entry.getValue() });
}
With the HashMap sorting gets also easier as you do not need to handle two independent array lists.
First approach: As you will work with JTable, Use TableRowSorter as shown in the demo example of tutorial page
Second approach: Assuming you need it not only for JTable
Object[][] array = {{"1", ".zip", "8"},{"2", ".java", "5"}, {"3", ".docx", "2"}};
List<Object[]>data = Arrays.asList(array);
Comparator<Object[]>comparator = new Comparator<Object[]>() {
#Override
public int compare(Object[] o1, Object[] o2) {
return ((String)o1[2]).compareTo((String)o2[2]);
}
};
Collections.sort(data, Collections.reverseOrder(comparator));
array = (Object[][]) data.toArray();

Treemap with <Integer, List>

I'm going count the most used words in a text and I want to make it this way just need little help how i'm gonna fix the Treemap..
this is how its look like now ...
TreeMap<Integer, List<String>> Word = new TreeMap<Integer, List<String>>();
List<String> TheList = new ArrayList<String>();
//While there is still something to read..
while (scanner.hasNext()) {
String NewWord = scanner.next().toLowerCase();
if (Word.containsKey(NewWord)) {
Word.put(HERE I NEED HELP);
} else {
Word.put(HERE I NEED HELP);
}
}
So what i wanna do is if the NewWord is in the list then add one on Integer(key) and if not Add the word to the next list.
Your type appears to be completely incorrect
... if you want a frequency count
You want to have your word as the key and the count as the value. There is little value in using a sorted collection, but it is many time slower so I would use a HashMap.
Map<String, Integer> frequencyCount = new HashMap<>();
while (scanner.hasNext()) {
String word = scanner.next().toLowerCase();
Integer count = frequencyCount.get(word);
if (count == null)
frequencyCount.put(word, 1);
else
frequencyCount.put(word, 1 + count);
}
... if you want to key by length. I would use a List<Set<String>> This is because your word length is positive and bounded, and you want to ignore duplicate words which is something a Set is designed to do.
List<Set<String>> wordsByLength = new ArrayList<Set<String>>();
while (scanner.hasNext()) {
String word = scanner.next().toLowerCase();
// grow the array list as required.
while(wordsByteLength.size() <= word.length())
wordsByLength.add(new HashSet<String>());
// add the word ignoring duplicates.
wordsByLength.get(words.length()).add(word);
}
All the examples above are correctly storing the count into a map, unfortunately they are not sorting by count which is a requirement you also have.
Do not use a TreeMap, instead use a HashMap to build up the values.
Once you have the complete list of values built you can then drop the entrySet from the HashMap into a new ArrayList and sort that array list by Entry<String,Integer>.getValue().
Or to be neater create a new "Count" object which has both the word and the count in and use that.
Dont do..
TreeMap<Integer, List<String>>
instead do,
TreeMap<String, Integer> // String represents the word... Integer represents the count
because your key (count) can be same sometimes where as the words will be unique...
Do it the other way around... keep reading the words and check if your map contains that word... If yes, increment the count, else add the word with count = 1.
Try this one
TreeMap<String, Integer> Word = new TreeMap<String,Integer>();
while (scanner.hasNext()) {
String NewWord = scanner.next().toLowerCase();
if (Word.containsKey(NewWord)) {
Word.put(NewWord,Word.get(NewWord)+1);
} else {
Word.put(NewWord,1);
}
}
The way to solve this in a time-efficient manner is to have two maps. One map should be from keys to counts, and the other from counts to keys. You can assemble these in different passes. The first should assemble the map from keys to counts:
Map<String, Integer> wordCount = new HashMap<String,Integer>();
while (scanner.hasNext()) {
String word = scanner.next().toLowerCase();
wordCount.put(word, wordCount.containsKey(word) ? wordCount.get(word) + 1 : 1);
}
The second phase inverts the map so that you can read off the top-most keys:
// Biggest values first!
Map<Integer,List<String>> wordsByFreq = new TreeMap<Integer,List<String>>(new Comparator<Integer>(){
public int compare(Integer a, Integer b) {
return a - b;
}
});
for (Map.Entry<String,Integer> e : wordCount) {
List<String> current = wordsByFreq.get(e.getValue());
if (current == null)
wordsByFreq.put(e.getValue(), current = new ArrayList<String>());
current.add(e.getKey());
}
Note that the first stage uses a HashMap because we don't need the order at all; just speedy access. The second stage needs a TreeMap and it needs a non-standard comparator so that the first value read out will be the list of most-frequent words (allowing for two or more words to be most-frequent).
Try this out:
TreeMap<String, Integer> map = new TreeMap<String, Integer>();
Scanner scanner = null;
while (scanner.hasNext()) {
String NewWord = scanner.next().toLowerCase();
if (map.containsKey(NewWord)) {
Integer count = map.get(NewWord);
// Add the element back along with incremented count
map.put(NewWord, count++);
} else {
map.put(NewWord,1); // Add a new entry
}
}

Java Parsing Using Hmap

I am new to Java. I want to Parse the data which is in this Format
Apple;Mango;Orange:1234;Orange:1244;...;
There could be more than one "Orange" at any point of time. Numbers (1,2...) increase and accordingly as the "Orange".
Okay. After splitting it, Lets assume I have stored the first two data(Apple, Orange) in a variable(in setter) to return the same in the getter function. And now I want to add the value(1234,1244....etc) in the 'orange' thing into a variable to return it later. Before that i have to check how many oranges have come. For that, i know i have to use for loop. But don't know how to store the "Value" into a variable.
Please Help me guys.
String input = "Apple;Mango;Orange:1234;Orange:1244;...;"
String values[] = input.split(";");
String value1 = values[0];
String value2 = values[1];
Hashmap< String, ArrayList<String> > map = new HashMap<String, ArrayList<String>>();
for(int i = 2; i < values.length; i = i + 2){
String key = values[i];
String id = values[i+1];
if (map.get(key) == null){
map.put(key, new ArrayList<String>());
}
map.get(key).add(id);
}
//for any key s:
// get the values of s
map.get(s); // returns a list of all values added
// get the count of s
map.get(s).size(); // return the total number of values.
Let me try to rephrase the question by how I interpreted it and -- more importantly -- how it focuses on the input and output (expectations), not the actual implementation:
I need to parse the string
"Apple;Mango;Orange:1234;Orange:1244;...;"
in a way so I can retrieve the values associated (numbers after ':') with the fruits:
I should receive an empty list for both the Apple and Mango in the example, because they have no value;
I should receive a list of 1234, 1244 for Orange.
Of course your intuition of HashMap is right on the spot, but someone may always present a better solution if you don't get too involved with the specifics.
There are a few white spots left:
Should the fruits without values have a default value given?
Should the fruits without values be in the map at all?
How input errors should be handled?
How duplicate values should be handled?
Given this context, we can start writing code:
import java.util.*;
public class FruitMarker {
public static void main(String[] args) {
String input = "Apple;Mango;Orange:1234;Orange:1244";
// replace with parameter processing from 'args'
// avoid direct implementations in variable definitions
// also observe the naming referring to the function of the variable
Map<String, Collection<Integer>> fruitIds = new HashMap<String, Collection<Integer>>();
// iterate through items by splitting
for (String item : input.split(";")) {
String[] fruitAndId = item.split(":"); // this will return the same item in an array, if separator is not found
String fruitName = fruitAndId[0];
boolean hasValue = fruitAndId.length > 1;
Collection<Integer> values = fruitIds.get(fruitName);
// if we are accessing the key for the first time, we have to set its value
if (values == null) {
values = new ArrayList<Integer>(); // here I can use concrete implementation
fruitIds.put(fruitName, values); // be sure to put it back in the map
}
if (hasValue) {
int fruitValue = Integer.parseInt(fruitAndId[1]);
values.add(fruitValue);
}
}
// display the entries in table iteratively
for (Map.Entry<String, Collection<Integer>> entry : fruitIds.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
}
}
If you execute this code, you will get the following output:
Mango => []
Apple => []
Orange => [1234, 1244]

Efficient way to delete values from hashmap object

I have HashMap object contains a key x-y-z with corresponding value test-test1-test2.
Map<String,String> map = new HashMap<String,String>();
map.put("x-y-z","test-test1-test2");
map.put("x1-y1-z1","test-test2-test3");
Now I have an input string array that contains some piece of the key:
String[] rem={"x","x1"}
Based on this string array I want to remove HashMap values.
Can anyone give an efficient approach to do this operation?
List remList = Arrays.asList(rem);
for (Iterator it = map.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
String[] tokens = key.split("-");
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
if (remList.contains(token)) {
it.remove();
break;
}
}
}
And an updated version with adding functionality based on your latest comment on this answer:
private static Map getMapWithDeletions(Map map, String[] rem) {
Map pairs = new HashMap();
for (int i = 0; i < rem.length; i++) {
String keyValue = rem[i];
String[] pair = keyValue.split("#", 2);
if (pair.length == 2) {
pairs.put(pair[0], pair[1]);
}
}
Set remList = pairs.keySet();
for (Iterator it = map.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
String[] tokens = key.split("-");
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
if (remList.contains(token)) {
it.remove();
pairs.remove(token);
break;
}
}
}
map.putAll(pairs);
return map;
}
Edited based on edited question.
Loop through the keySet of the hashmap. When you find a key that starts with x you are looking for remove it from the map.
Something like:
for(String[] key: map.keySet()){
if(key.length>0 && x.equals(key[0])){
map.remove(key);
}
}
Assuming I understand you correctly, and you want to remove everything starting with 'x-' and 'x1-' from the map (but not 'x1111-', even though 'x1' is a prefix of 'x1111'), and efficiency is important, you might want to look at one of the implementations of NavigableMap, such as (for example) TreeMap.
NavigableMaps keep their entries in order (by natural key order, by default), and can be iterated over and searched very efficiently.
They also provide methods like subMap, which can produce another Map which contains those keys in a specified range. Importantly, this returned Map is a live view, which means operations on this map affect the original map too.
So:
NavigableMap<String,String> map = new TreeMap<String,String>();
// populate data
for (String prefixToDelete : rem) {
// e.g. prefixToDelete = "x"
String startOfRange = prefixToDelete + "-"; // e.g. x-
String endOfRange = prefixToDelete + "`"; // e.g. x`; ` comes after - in sort order
map.subMap(startOfRange, endOfRange).clear(); // MAGIC!
}
Assuming your map is large, .subMap() should be much faster than iterating over each Map entry (as a TreeMap uses a red-black tree for fast searching).
You can do the following:
Map<String,String> map = new HashMap<String,String>();
map.put("x-y-z","test-test1-test2");
map.put("x1-y1-z1","test-test2-test3");
String[] rem={"x","x1"};
for (String s : rem) {
map.keySet().removeIf(key -> key.contains(s));
}
This piece of code will remove all entries with "x" or "x1" in the map key.

Categories

Resources