Implementation of data structure for "phone directory" - java

I have a phone directory with details like this:
ABc -> 123
bcd -> 345
cda -> 523
abc -> 678
So if I want to see ABc person's phone numbers, I should get the both numbers. How can we implement this in java? means which data structure will be the best?

If you are just looking for some data structure to handle this kind of data, I will offer using a HashMap of ArrayLists like this:
HashMap<String, ArrayList<Integer>> phoneNumber
Then you will be able to assign a dynamic array of numbers (like [123, 678] to each key (like "abc")
For this example, the code would be something like:
HashMap<String, ArrayList<Integer>> phoneNumber = new HashMap<String, ArrayList<Integer>>();
String key = "abc";
int value = 123;
if (phoneNumber.containsKey(key)) {
phoneNumber.get(key).add(value);
} else {
ArrayList<Integer> arrayList = new ArrayList<Integer>(1);
arrayList.add(value);
phoneNumber.put(key, arrayList);
}
Also if the letter case is important in this example, consider using String.toLowerCase() function, which makes the second line of code above, like this:
String key = "abc".toLowerCase();

Related

Insert string keyword values based map keys in main string

In Java, I have to insert strings value based on the key in main string.
For example -
Main String -
sellers(seller: $sellerId, shipment: $shipmentId)
Map of key and value -
{
sellerId: abc
shipmentId: 123
}
So after inserting it will become
sellers(seller: abc, shipment: 123)
I know i can do string replace. But that doesn't seem to be good approach here. So just wondering is there a standard approach or better way of doing things here?
Two approaches you can consider:
1 - loop over map entries, and do a simple string replace (note that this assumes a single occurrence of each var in the strings; if that is not the case, you need to use replaceAll):
String text = "sellers(seller: $sellerId, shipment: $shipmentId)";
Map<String, Object> binding = ...;
String result = text;
for (Entry<String, Object> entry : binding.entrySet()) {
result = result.replace("$" + entry.getKey(), String.valueOf(entry.getValue()));
}
2 - for advanced use cases, you want to use a proper template engine. And here's an example using groovy's simple template engine (use in java by adding the groovy jar):
groovy.text.SimpleTemplateEngine engine = new groovy.text.SimpleTemplateEngine();
Writable template = engine.createTemplate(text).make(binding);
String result = template.toString();
Just note that groovy replaces variable names prefixed with $, and that's why this works without changes (making this a good choice for your current syntax).
Both produce your expected result, but you have to choose based on what this can turn into.
Depending on values map can hold you may face some problems. For instance if value may contain other key identifier like
{
foo: $bar
bar: 123
}
then using series of replace(mapEntryKey, mapEntryValue) could change string like
abc $foo efg $bar
first into $foo->$bar
abc $bar efg $bar
and then $bar->123
abc 123 efg 123
which is NOT what we wanted.
To avoid such problem we should iterate over template only once, search for each $key and replace it with value stored for it in map. If map doesn't contain such key we can leave it as it (replace it with itself).
We can do it with Matcher#replaceAll​(Function<MatchResult,​String> replacer). BTW if map value can contain $ and \ which are also metacharacters in replacement, we need to escape them. To do it we can use Mather#quoteReplacement method.
Demo:
Map<String, String> map = Map.of("sellerId", "abc",
"shipmentId", "123");
String yourTemplate = "sellers(seller: $sellerId, shipment: $shipmentId)";
Pattern p = Pattern.compile("\\$(\\w+)");
Matcher m = p.matcher(yourTemplate);
String replaced = m.replaceAll(match -> {
if(map.containsKey(match.group(1))){
return Matcher.quoteReplacement(map.get(match.group(1)));
}else{
return Matcher.quoteReplacement(match.group());
}
});
System.out.println(replaced);
Output: sellers(seller: abc, shipment: 123).
String format is an option here
Map<String, Integer> yourMap = new HashMap<>();
yourMap.put("abc", 123);
for (Map.Entry<String, Integer> entry : yourMap.entrySet()) {
String output = String.format("sellers(seller: %s, shipment: %d)", entry.getKey(), entry.getValue());
System.out.println("output = " + output);
}

Retrieve String from values in HashMap at a specific occurrence of special character

So I'm trying retrieve specific substrings in values in a Hashmap constructed like this..
HashMap<ID, "Home > Recipe > Main Dish > Chicken > Chicken Breasts">
Which is passed from a different method that returns a HashMap
In above example, I need to retrieve Chicken.
Thus far, I have..
public static ArrayList<String> generalize() {
HashMap<String, String> items = new HashMap<>();
ArrayList<String> cats = new ArrayList<>();
items = RecSys.readInItemProfile("PATH", 0, 1);
for(String w : items.values()) {
cats.add(w);
}
for(String w : cats) {
int e = w.indexOf('>', 1 + w.indexOf('>', 1 + w.indexOf('>')));
String k = w.substring(e+1);
System.out.print(k);
e = 0;
}
System.out.println("k" + cats);
return cats;
}
Where I try to nullify String e for each iteration (I know it's redundant but it was just to test).
In my dataset, the first k-v pair is
3880=Home  >  Recipes  >  Main Dish  >  Pasta,
My output is
Pasta
Which is ok. If there are more than 3x ">", it'll return all following categories. Optimally it wouldn't do that, but it's ok if it does. However, further down the line, it (seemingly) randomly returns
Home > Recipe
Along with the rest of the data...
This happens at the 6th loop, I believe.
Any help is greatly appreciated..
Edit:
To clarify, I have a .csv file containing 3 columns, whereas 2 are used in this function (ID and Category). These are passed to this function by a read method in another class.
What I need to do is extract a generalized description of each category, which in all cases is the third instance of category specification (that is, always between the third and fourth ">" in every k-v pair).
My idea was to simply put all values in an arraylist, and for every value extract a string from between the third and fourth ">".
I recommend using the following map:
Map<Integer, List> map = new HashMap<>();
String[] vals = new String[] { "HomeRecipe", "Main Dish", "Chicken",
"Chicken Breasts" };
map.put(1, Arrays.asList(vals));
Then, if you need to find a given value in your original string using an ID, you can simply call ArrayList#get() at a certain position. If you don't care at all about order, then a map of integers to sets might make more sense here.
If you can. change your data structure to a HashMap<Integer, List<String>> or HashMap<Integer, String[]>. It's better to store the categories (by cats you mean categories right?) in a collection instead of a string.
Then you can easily get the third item.
If this is not possible. You need to do some debugging. Start by printing every input and output pair and find out which input caused the unexpected output. Your indexOf method seems to work at first glance.
Alternatively, try this regex method:
String k = cats.replaceAll("(?:[^>]+\\s*>\\s*){3}([^>]+).*", "$1");
System.out.println(k);
The regex basically looks for a xxx > yyy > zzz > aaa ... pattern and replaces that pattern with aaa (whatever that is in the original string).

Split a treeSet value set to arrays and then add to an array

I am fairly new to Java and I have been trying to use TreeSets, all was well however, I want to split the values of a specific key to indiviual string. i.e.
Alice,3,1,6,3,6
would become:
Alice is a string
3 is a string
1 is a string
and then I would like to add them to an array so that:
Alice,3,1,6,3,6
Jon,5,3,1,6,5
Alice and Jon are in ArrayNames
3 and 5 are in ArrayScore1
I basically need the value to split into their values and then transferred to an array
All help is welcomed and appreciated
Thanks
Silver
It's a little hard to tell, but I think you actually want to create a Map<String, List<String>> from a Set<String>.
Try this:
Set<String> set = new TreeSet<>(); // doesn't matter what type of Set
// NOTE: You must populate the list somehow
Map<String, List<String>> map = new LinkedHashMap<>();
set.stream()
.map(s -> s.split(",")) // String to String[]
.<String[], List<String>>map(Arrays::asList) // convert String[] to List<String>
.forEach(a -> map.put(a -> a.get(0), a -> a.subList(1, a.size())));
Avoid using arrays unless you absolutely have to: Always prefer using Collections.
If you only want to do one thing with the data (ie you don't need to keep the map), instead of collecting the data, call .forEach() instead, for example:
...
.forEach(s -> System.out.println(s.get(0) + " has scores " + s.subList(1, s.size()));
Disclaimer: Code above may not compile or work as it was thumbed in on my phone (but there's a reasonable chance it will work)

Best way to implement a Reverse MultiMap

My Java8 program has several stages:
A CSV file is parsed. The CSV file looks like this:
123,[Foo:true; Bar:true; Foobar:false; Barfoo:false]
456,[Foobar:true; Barfoo:false; Foo:false; Bar:false]
789,[Foobar:true; Barfoo:false; Foo:false]
where 123, 546 and 789 are unique identifiers of each datastructure, one datastructure is represented by the column identifiers Foo Bar Foobar and Barfoo, where each boolean indicates, if the column is a key-colum.
While the CSV file is parsed, for each line a datastructure must be created.
Later, in runtime, wich needs to be fast, the following will happen:
An ArrayList<String> containing column data is given. Data needs to be added to a specific datastructure. (I do now the unique identifier 123).
Say: the ArrayList<String> needs to be added to 123: 1-> foo, 2->bar, 3-> foobar, 4 ->barfoo.
Say: another ArrayList<String> needs to be added to 456: 1-> foobar, 2-> barfoo
Say: another ArrayList<String> needs to be added to 789: 1-> foobar, 2-> null, 3-> foo
The tasks that the datastructure needs to provide are the following:
add(ArrayList<String>) : void
remove(ArrayList<String>) : boolean (if successfull)
contains(Arraylist<String>) : boolean
get(ArrayList<String>) : ArrayList<String>
Notes:
The combination of all keys inside of one datastructure 123 are unique. Meaning: If in 123 is one entry with foo,bar,foobar, barfoo, Another enrty with foo,bar,doesnt matter, neither will not be allowed. Another enrty with foo1,bar, foobar,barfoo is allowed, as well as an entry with foo1,bar1,foobar, barfoo is also allowed.
It won't happen, that wile parsing, a column name not beeing a key (true), is in front of a key. This will not happen:
[Foobar:true;Barfoo:false;Foo:true;Bar:false]
It won't happen, at runtime, that a column marked as a key will not get data: This will not happen: an ArrayList<String> added to 123 with data looks like this: 1->foo, 2->null, 3->foobar.
I tried: storing at each datastructure-class two arrays. One with the Column Names, and one with Numbers of the Columns, which are keys. At runtime the key-indicating array will be processed to get all key values (at the first add example above it would be foo and bar) and they will be concatenated. (to a String "foo,bar"). This is a new key for a second HashMap<String,ArrayList<String>> where the value (ArrayList<String>) contains the data of all columns (foo,bar,foobar,barfoo).
I have a getKeyString method:
String getKeyString(ArrayList<String> keys, ArrayList<Integer> keyPos){
if (keyPos.get(keyPos.size()-1) >= keys.size()) //if the last entry from orders arraylist keyPos is greater than size of keys
throw new Exception();
String collect = keyPos.stream().map(i -> keys.get(i))
.map(string ->{
try{
if(string.equals("null")) // happens not very often, ~1time in 1,000
return "";
}
catch(NullPointerException e) { //happens even less 1 in 100,000
return "";
}
return string;
})
.collect(Collectors.joining(","));
if(collect.length()<keyPos.size())
throw new Exception("results in an empty key: ");
return collect;
and the addDataListEnty looks quite similar to this:
HashMap<String, ArrayList<String>> dataLists = new HashMap<>();
ArrayList<Integer> keyPos = new ArrayList<>();
...
public void addDataListEntry(ArrayList<String> values) {
// will overwrite Entry if it already exists
try {
this.dataLists.put(getKeyString(values, keyPos), values);
} catch (Exception e) {
logger.info(e.getMessage());
}
}
This does actually work, but is really slow, since the key (foo,bar) needs to created at every datastructure-access.
Which combination of HashMaps, Lists, Sets, (even Google Guava) is the best to make it as fast as possible?

Can you reference a java variable from a string?

Hi I have a strange question about java. I will leave out the background info so as not to complicate it. If you have a variable named fname. And say you have a function returning a String that is "fname". Is there a way to say reference the identifier fname via the String "fname". The idea would be something like "fname".toIdentifier() = value but obviously toIdentifier isn't a real method.
I suppose a bit of background mite help. Basically I have a string "fname" mapped to another string "the value of fname". And I want a way to quickly say the variable fname = the value of the key "fname" from the map. I'm getting the key value pair from iterating over a map of cookies in the form . And I don't want to do "if key = "fname" set fname to "value of fname" because I have a ton of variables that need to be set that way. I'd rather do something like currentkey.toIdentifer = thevalue. Weird question maybe I'm overlooking a much easier way to approach this.
Why don't you just use a simple hashmap for this?
Map<String, String> mapping = new HashMap<String, String>();
mapping.put("fname", "someValue");
...
String value = mapping.get(key); //key could be "fname"
In a way you're describing what reflection is used for:
You refer to an object's fields and methods by name.
Java Reflection
However, most of the time when people ask a question like this, they're better off solving their problem by re-working their design and taking advantage of data structures like Maps.
Here's some code that shows how to create a Map from two arrays:
String[] keyArray = { "one", "two", "three" };
String[] valArray = { "foo", "bar", "bazzz" };
// create a new HashMap that maps Strings to Strings
Map<String, String> exampleMap = new HashMap<String, String>();
// create a map from the two arrays above
for (int i = 0; i < keyArray.length; i++) {
String theKey = keyArray[i];
String theVal = valArray[i];
exampleMap.put(theKey, theVal);
}
// print the contents of our new map
for (String loopKey : exampleMap.keySet()) {
String loopVal = exampleMap.get(loopKey);
System.out.println(loopKey + ": " + loopVal);
}
Here's a link to the JavaDoc for Map.

Categories