Is there a data structure like the Java Set in JavaScript? [duplicate] - java

This question already has answers here:
Does JavaScript have an implementation of a set data structure?
(6 answers)
Closed 8 years ago.
I want to use a data structure in JavaScript that can be used to store number of IDs. I should be able to check if a key already exists in that set, something like Java Sets.
I want to achive same behaviours as follows (this code is in Java):
Set<String> st = new HashSet<String>();
//add elemets
if(st.contains("aks") ){
//do something
}
I want a JavaScript/dojo equivalent of the above code.

I've written a JavaScript HashSet implementation that does what you want and allows any object to be a member of the set: http://code.google.com/p/jshashtable
However, if you just need to store strings, you could do something more simply by storing set members as property names of a normal Object. For example:
function StringSet() {
var setObj = {}, val = {};
this.add = function(str) {
setObj[str] = val;
};
this.contains = function(str) {
return setObj[str] === val;
};
this.remove = function(str) {
delete setObj[str];
};
this.values = function() {
var values = [];
for (var i in setObj) {
if (setObj[i] === val) {
values.push(i);
}
}
return values;
};
}
A note about the implementation: val is an object used internally by the StringSet implementation that is unique to each set. Comparing property values of the object whose property names make up the set (setObj) against val eliminates the need for a hasOwnProperty() check and guarantees that only strings that have been added to the set will show up in values.
Example usage:
var set = new StringSet();
set.add("foo");
set.add("bar");
alert(set.contains("foo")); // true
alert(set.contains("baz")); // false
set.values(); // ["foo", "bar"], though not necessarily in that order
set.remove("foo");
set.values(); // ["bar"]

Why not use a normal object and check if a key exists with JavaScript's hasOwnProperty?
var x = {};
x['key'] = 'val';
x.hasOwnProperty('key'); // true //
x.hasOwnProperty('key2'); // false //
And here is a more advanced use case:
var x = {};
var prefix = 'item_';
for(var i=0;i<10;i++){
x[prefix+i] = 'value '+(i+1);
}
x.hasOwnProperty('item_6'); // true //
x.hasOwnProperty('other key'); // false //
Removing items can be done like this:
delete x['key'];

No Dojo needed, this is native to Javascript. Use Objects. Sounds like you only need the keys, not the values. Lookup is constant time.
var st = {'aks':1, 'foo':1, 'bar':1}; // or could start with empty {}. 1 could be any value of any type, it's just short.
//add elements
st.baz = 1;
//or load up dynamically
myArrayOfStrings.forEach(function(key){
st[key] = 1;
});
if("aks" in st){
//do something
}

Possibly with an associative array / Hashtable / dictionary (I don't know how it's called exactly), using the set elements as keys and "anything else" as values.
insert: mySet[key] = "Whatever";
delete: mySet[key] = null;
check: if (mySet[key] != null) { ... }

Hash is good candidate for implementing Set. You could create a set using a function like that:
function set () {
var result = {};
for (var i = 0; i < arguments.length; i++) result[arguments[i]] = true;
return result;
}
For instance:
x = set([1,2,2,4])
x[1] #==> true
x[3] #==> false
x[5] = true; # add element to the set
x[5] = false; # remove element from the set

Sets don't have keys. They only have set of values, but maps have pairs of key/value entities.
As a result, you have 2 options. Each of them has its drawbacks and advantages:
You can use as described above JavaScript object. Actually it is a map/associative array/hash table. One of its advantage - you can guarantee with this kind of structure that keys - are unique items. Its drawback connected to the issue - you have to keep some extra information that you don't need at all. Values of maps. trues or some other values. It does not matter. Why do you need them?
To resolve the previous disadvantage you may consider using JavaScript arrays. But, you'll have to write some wrappers so arrays's behavior will look like sets behavior. Also operations that will search by the uniqueId will be slower than the same ones for hashtables cause you'll have to iterate via all items of an array.
So, I think you should prefer hashtables to arrays, examples you can find in other posts. But probably you should consider changing of your data structure. don't keep uniqueId as keys with unselerss values if its possible. Let your unique ids point to some real objects for which these unique ids are used.
PS: one more thing. Arrays are also objects actually. As a result they can be used as hashtables/maps too.

Related

read the value from nested foreach loop

I have two String Array, i have to enter the value from the second array while the first array element is used to find webelement.
Here is the sample code:
public void isAllTheFieldsDisplayed(String values, String fields) {
String[] questions = fields.split(",");
String[] answers = values.split(",");
for(String q : questions) {
// HERE IS THE PROBLEM - I want only the first answer from the String[] answers. similarly for the second question, i want the second element from the String[] answers.
// THIS WONT WORK - for(string ans : answers)
find(By.cssSelector("input[id='"+q+"']")).sendKeys(ans);
}
}
You probably need to check whether the two arrays contain the same number of elements.
Utilising a simple integer for loop and slice the elements from the arrays:-
for(int i=0; i<questions.length; i++ {
driver.findElement(By.id(questions[i])).sendKeys(answers[i]);
}
I assume the find method is some sort of wrapper for selenium's findElement
As id is being located suggest using By.id?
Ideally check whether a WebElement is found before calling sendKeys
Here's a slightly different approach. Which could be overkill depending on your environment.
Because of the coupled relationship of your questions and answers, we want to make sure they get paired correctly, and once they're paired there's no reason to distribute them separately anymore.
This could be a re-usable utility function like so:
public Map<String, String> csvsToMap(String keyCsv, String valueCsv) {
String[] questions = keyCsv.split(",");
String[] answers = valueCsv.split(",");
// This could also be something like "questions.length >= answers.length" so if there
// are more questions than answers the extras would be ignored rather than fail....
if (questions.length != answers.length) { // fail fast and explicit
throw new RuntimeException("Not the same number of questions and answers");
}
Map<String, String> map = new HashMap<>();
for (int i = 0; i < questions.length; i++) {
map.put(questions[i], answers[i]);
}
return map;
}
After the data has been sanitized and prepared for ingesting, handling it becomes a bit easier:
Map<String, String> preparedQuestions = csvsToMap(values, fields);
for (String aQuestion : preparedQuestions.keySet()) {
String selector = "input[id='" + aQuestion + "']";
String answer = preparedQuestions.get(aQuestion);
driver.findElement(By.id(selector)).sendKeys(answer);
}
Or if java8, streams could be used:
csvsToMap(values, fields).entrySet().stream()
.forEach(pair -> {
String selector = "input[id='" + pair.getKey() + "']";
driver.findElement(By.id(selector)).sendKeys(pair.getValue());
});
Preparing your data in a function like this ahead of time lets you avoid using indexes altogether elsewhere. If this is a pattern you repeat, a helper function like this becomes a single point of failure, which lets you test it, gain confidence in it, and trust that there aren't other near-identical snippets elsewhere that might have subtle differences or bugs.
Note how this helper function doesn't have any side effects, as long as the same inputs are provided, the same output should always result. This makes it easier to test than it would be having webdriver operations baked into this task, as webdriver has built in side-effects which can fail at any time with no fault to your code. (aka talking to the browser)
Iterator may resolve this, But i haven't tried.
Iterator itr = questions.iterator();
Iterator itrans = answers.iterator();
while( itr.hasNext() && itrans.hasNext())

HashMap<data class, data class> object.keySet() function throws error relating to a public call

I have a data class:
data class UniqueProtein(var id: String, var spc: Int, var r: DoubleArray)
I've created a HashMap<UniqueProtein, UniqueProtein> that I want to convert to a HashSet so I can find overlapping values between two HashMaps, like so:
dottest(control: HashMap<UniqueProtein, UniqueProtein>, treatment: HashMap<UniqueProtein, UniqueProtein>)
var combineddata:HashSet<UniqueProtein> = control.keySet().retainAll(treatment.keySet())
IntelliJ is throwing the following in-line error under both keySet() calls:
is public /*package*/ in 'HashMap'
But since I've declared the object as a data class, I'm not sure what's going on...
If you're looking for the common keys in two maps, you can just intersect their keys, like so:
val combinedData: Set<UniqueProtein> = control.keys.intersect(treatment.keys)
This returns a new set containing the common elements of the two sets.
And if you jump into its implementation, you'll see that it just uses retainAll (on a copy of the set that it's called on):
public infix fun <T> Iterable<T>.intersect(other: Iterable<T>): Set<T> {
val set = this.toMutableSet()
set.retainAll(other)
return set
}
You can access the key set through the keys member instead.
Then to filter the keys that are part of the second map:
val combinedData = control.keys.filter { treatment.keys.contains(it) }.toSet()
To filter the whole map by keys (to get back the entries and not just the keys):
var filteredMapByKeys = control.filterKeys { treatment.keys.contains(it) }
To filter the values based by the keys:
var filteredValuesByKeys = control.filterKeys { treatment.keys.contains(it) }.values.toSet()

Arraylist element name already exists, update otherwise create new one in java

I have trying to get the string value from the arraylist values.
But In my arraylist if the value already exists , just update the count . otherwise need to create a new one. Here values are updating, but how can i create a new one element if the name didn't match with the arraylist element name? Please tell me , how can i verify the element(GlobalData.getCRole) already exists in the arraylist.
In this code the arraylist name is GlobalData.getrolecount
GlobalData.getCRole = item.getRole_name();
if (GlobalData.getrolecount.size() > 0) {
for (int i = 0; i < GlobalData.getrolecount.size(); i++) {
Role getrc = GlobalData.getrolecount.get(i);
Role getrcverify = new Role();
getrcverify.setRole_name(GlobalData.getCRole);
if (getrc.getRole_name().equalsIgnoreCase(GlobalData.getCRole)) {
String inccount = GlobalData.getrolecount.get(i).getCount();
int getcount = Integer.parseInt(inccount) + 1;
getrc.setCount(Integer.toString(getcount));
}
}
} else {
Role getrc = new Role();
getrc.setRole_name(GlobalData.getCRole);
getrc.setCount("1");
GlobalData.getrolecount.add(getrc);
}
Adding to #GabeSechan's answer, here's a snippet that would help you:
//let's say you store your data in a Map called myHashMap
String keyToMatch = "your_key_here"; // replace this line with whatever code you use to get your key
if(myHashMap.containsKey(keyToMatch)) // Check if your map already contains the key
{
int val = myHashMap.get(keyToMatch);
myHashMap.put(keyToMatch, val++); // Can be shrunken to a single line
}
else
{
myHashMap.put(keyToMatch, 1); // If it doesn't exist in the map, add it (with count 1)
}
This code can be shrunken much more because Map<> is a very robust tool. But I've written it in a way similar to your implementation so you can understand it better.
Let me know if you need further explanation or help trying to init myHashMap.
I'd use a different data structure. A list isn't what you want. You want a Map (probably a HashMap) of strings to Count, or of strings to Roles. That way you can do O(1) searches to see if a role already exists, rather than an O(n) walk of the list, and checking if the role exists is a simple check to see if get returns null.

Check value inside Map

I have a Map where I save values with the form NAME-GROUP.
Before doing some operations, I need to know if the Map contains a specific group,
for example: I need to check for values containing group1 like Mark-group1.
I'm trying to get it this way:
if (checkList.containsValue(group1)) {
exists = true;
}
I can't provide the name when searching because there could be diferent names with the same group.
But it isn't finding the value, as seems that this function just looks for the entire value string and not only for part of it.
So, there would be any way of achieving this, or would I need to change the way I'm focusing my code.
Update--
This is the looking of my Map:
Map<Integer, String> checkList = new HashMap<Integer, String>();
I load some values from a database and I set them into the Map:
if (c.moveToFirst()) {
int checkKey = 0;
do {
checkKey++;
checkList.put(checkKey, c.getString(c.getColumnIndex(TravelOrder.RELATION)));
}while(c.moveToNext());
}
The relation column, has values like: mark-group1, jerry-group1, lewis-group2, etc...
So, the Map will have a structure like [1, mark-group1], etc...
What I need is to check if there is any value inside the map that contains the string group1 for example, I don't care about the name, I just need to know if that group exists there.
If you want to check any value contain your string as a substring you have to do the following:
for (String value : yourMap.values()) {
if (value.contains(subString)) {
return true;
}
}
return false;
By the way if your values in the map are really have two different parts, i suggest to store them in a structure with two fields, so they can be easily searched.

how to manipulate list in java

Edit: My list is sorted as it is coming from a DB
I have an ArrayList that has objects of class People. People has two properties: ssn and terminationReason. So my list looks like this
ArrayList:
ssn TerminatinoReason
123456789 Reason1
123456789 Reason2
123456789 Reason3
568956899 Reason2
000000001 Reason3
000000001 Reason2
I want to change this list up so that there are no duplicates and termination reasons are seperated by commas.
so above list would become
New ArrayList:
ssn TerminatinoReason
123456789 Reason1, Reason2, Reason3
568956899 Reason2
000000001 Reason3, Reason2
I have something going where I am looping through the original list and matching ssn's but it does not seem to work.
Can someone help?
Code I was using was:
String ssn = "";
Iterator it = results.iterator();
ArrayList newList = new ArrayList();
People ob;
while (it.hasNext())
{
ob = (People) it.next();
if (ssn.equalsIgnoreCase(""))
{
newList.add(ob);
ssn = ob.getSSN();
}
else if (ssn.equalsIgnoreCase(ob.getSSN()))
{
//should I get last object from new list and append this termination reason?
ob.getTerminationReason()
}
}
To me, this seems like a good case to use a Multimap, which would allow storing multiple values for a single key.
The Google Collections has a Multimap implementation.
This may mean that the Person object's ssn and terminationReason fields may have to be taken out to be a key and value, respectively. (And those fields will be assumed to be String.)
Basically, it can be used as follows:
Multimap<String, String> m = HashMultimap.create();
// In reality, the following would probably be iterating over the
// Person objects returned from the database, and calling the
// getSSN and getTerminationReasons methods.
m.put("0000001", "Reason1");
m.put("0000001", "Reason2");
m.put("0000001", "Reason3");
m.put("0000002", "Reason1");
m.put("0000002", "Reason2");
m.put("0000002", "Reason3");
for (String ssn : m.keySet())
{
// For each SSN, the termination reasons can be retrieved.
Collection<String> termReasonsList = m.get(ssn);
// Do something with the list of reasons.
}
If necessary, a comma-separated list of a Collection can be produced:
StringBuilder sb = new StringBuilder();
for (String reason : termReasonsList)
{
sb.append(reason);
sb.append(", ");
}
sb.delete(sb.length() - 2, sb.length());
String commaSepList = sb.toString();
This could once again be set to the terminationReason field.
An alternative, as Jonik mentioned in the comments, is to use the StringUtils.join method from Apache Commons Lang could be used to create a comma-separated list.
It should also be noted that the Multimap doesn't specify whether an implementation should or should not allow duplicate key/value pairs, so one should look at which type of Multimap to use.
In this example, the HashMultimap is a good choice, as it does not allow duplicate key/value pairs. This would automatically eliminate any duplicate reasons given for one specific person.
What you might need is a Hash. HashMap maybe usable.
Override equals() and hashCode() inside your People Class.
Make hashCode return the people (person) SSN. This way you will have all People objects with the same SSN in the same "bucket".
Keep in mind that the Map interface implementation classes use key/value pairs for holding your objects so you will have something like myHashMap.add("ssn",peopleobject);
List<People> newlst = new ArrayList<People>();
People last = null;
for (People p : listFromDB) {
if (last == null || !last.ssn.equals(p.ssn)) {
last = new People();
last.ssn = p.ssn;
last.terminationReason = "";
newlst.add(last);
}
if (last.terminationReason.length() > 0) {
last.terminationReason += ", ";
}
last.terminationReason += p.terminationReason;
}
And you get the aggregated list in newlst.
Update: If you are using MySQL, you can use the GROUP_CONCAT function to extract data in your required format. I don't know whether other DB engines have similar function or not.
Update 2: Removed the unnecessary sorting.
Two possible problems:
This won't work if your list isn't sorted
You aren't doing anything with ob.getTerminationReason(). I think you mean to add it to the previous object.
EDIT: Now that i see you´ve edited your question.
As your list is sorted, (by ssn I presume)
Integer currentSSN = null;
List<People> peoplelist = getSortedList();//gets sorted list from DB.
/*Uses foreach construct instead of iterators*/
for (People person:peopleList){
if (currentSSN != null && people.getSSN().equals(currentSSN)){
//same person
system.out.print(person.getReason()+" ");//writes termination reason
}
else{//person has changed. New row.
currentSSN = person.getSSN();
system.out.println(" ");//new row.
system.out.print(person.getSSN()+ " ");//writes row header.
}
}
If you don´t want to display the contents of your list, you could use it to create a MAP and then use it as shown below.
If your list is not sorted
Maybe you should try a different approach, using a Map. Here, ssn would be the key of the map, and values could be a list of People
Map<Integer,List<People>> mymap = getMap();//loads a Map from input data.
for(Integer ssn:mymap.keyset()){
dorow(ssn,mymap.get(ssn));
}
public void dorow(Integer ssn, List<People> reasons){
system.out.print(ssn+" ");
for (People people:reasons){
system.out.print(people.getTerminationReason()+" ");
}
system.out.println("-----");//row separator.
Last but not least, you should override your hashCode() and equals() method on People class.
for example
public void int hashcode(){
return 3*this.reason.hascode();
}

Categories

Resources