I have an arraylist that looks like this:
public static ArrayList<ArrayList<String[]>> x = new ArrayList<>();
I store groups of 2 persons in a pair. For example:
[Person1, Person2]
[Person3, Person4]
The algorithm I use right now still makes duplicates, I've tried out hashmaps and iterating through them with for loop but they just give me back the original list.
This is the code:
package com.company;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
public class createGroups
{
public static ArrayList<ArrayList<String[]>> x = new ArrayList<>();
public static void main(String[] args){
//Define names
String[] names = {"Person1", "Person2", "Person3", "Person4"};
try
{
//Create combinations. In a try catch because of the saveFile method.
combination(names, 0, 2);
//Print all the pairs in the Arraylist x
printPairs();
} catch (IOException e)
{
e.printStackTrace();
}
}
static void combination(String[] data, int offset, int group_size) throws IOException
{
if(offset >= data.length)
{
//Create new Arraylist called foo
ArrayList<String[]> foo = new ArrayList<>();
//Create a pair of 2 (data.length = 4 / group_size = 2)
for(int i = 0; i < data.length / group_size; i++)
{
//Add the pair to foo.
foo.add(Arrays.copyOfRange(data, 2 * i, 2 * (i + 1)));
}
//Add foo to x
x.add(foo);
//saveFile(foo);
}
for(int i = offset; i < data.length; i++){
for(int j = i + 1; j < data.length; j++){
swap(data, offset, i);
swap(data, offset + 1, j);
combination(data, offset + group_size, group_size);
swap(data, offset + 1, j);
swap(data, offset, i);
}
}
}
public static void printPairs(){
//Print all pairs
for(ArrayList<String[]> q : x){
for(String[] s : q){
System.out.println(Arrays.toString(s));
}
System.out.println("\n");
}
}
private static void swap(String[] data, int a, int b){
//swap the data around.
String t = data[a];
data[a] = data[b];
data[b] = t;
}
}
The output right now is this:
Output
Every group of 4 names is a 'list' of pairs (Not really a list but that's what I call it)
And this is the desired output:
Desired output
But then you can see that the first and the last list of pairs are basically the same how do I change that in my combination method
The question:
How can I change my combination method so that it doesn't create duplicate groups.
And how can I make the list smaller (The desired output) when printing the created lists.
If I wasn't clear enough or if I didn't explain what I want very well, let me know. I'll try to make it clearer.
Create an object similar to this. It takes 4 strings (2 pairs). Puts the strings into array and sorts this array. That means any combination of strings you put in will be converted into one sorted combination, but the object internaly remembers which person is person1, person2, ...
private class TwoPairs {
private final String person1;
private final String person2;
private final String person3;
private final String person4;
private final String[] persons;
TwoPairs(String person1, String person2, String person3, String person4) {
this.person1 = person1;
this.person2 = person2;
this.person3 = person3;
this.person4 = person4;
persons = new String[4];
persons[0] = person1;
persons[1] = person2;
persons[2] = person3;
persons[3] = person4;
// if we sort array of persons it will convert
// any input combination into single (sorted) combination
Arrays.sort(persons); // sort on 4 objects should be fast
// hashCode and equals will be comparing this sorted array
// and ignore the actual order of inputs
}
// compute hashcode from sorted array
#Override
public int hashCode() {
return Arrays.hashCode(persons);
}
// objects with equal persons arrays are considered equal
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
TwoPairs other = (TwoPairs) obj;
if (!Arrays.equals(persons, other.persons)) return false;
return true;
}
// add methods which you might need
// getters for individual persons
// String getPerson1() { return person1; }
// or perhaps pairs of persons
// String[] getPair1() { return new String[] {person1, person2}; }
// add sensible toString method if you need it
}
Your ArrayList x will change like this
ArrayList<TwoPairs> x = new ArrayList<TwoPairs>();
before adding new TwoPairs object into x check if this list already contains this object.
if (!x.contains(twoPairsObject)) {
x.add(twoPairsObject);
}
Related
I am trying to find the longest possible path based on how many connections a variable number has, without repeating connections. The way I thought of doing this was creating a list that holds all points that have already been gone through, but when a path ends, and I need to check a new path, all of those old connections remain in the list. How can I restart my list from the initial point?
Putting it in the recursive function itself would just clear the list each time. Is there a better option than using a list?
Relevant code:
package testapp;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.io.IOException;
import java.util.List;
class TestApp {
// Store list of objects we have already matched with
static List<NumberObject> holdingList = new ArrayList<NumberObject>();
//Test objects
static int[] array1 = {2,2};
static int[] array2 = {3,1};
static int[] array3 = {2,1};
static int[] array4 = {1,1};
static NumberObject eight = new NumberObject(array1, 8);
static NumberObject two = new NumberObject(array2, 2);
static NumberObject three = new NumberObject(array3, 3);
static NumberObject four = new NumberObject(array4, 4);
// Test objects ^^
public static int longestSequence(int[][] grid) {
// TODO: implement this function
// Code exists here not relevant to the problem
//Setting up a new numberList array for testing
NumberObject[] newNumberList = {eight, two, three, four};
NumberObject[] connections1 = {two, four};
NumberObject[] connections2 = {two, three};
//Adding connections
eight.connections = connections1;
four.connections = connections2;
for (NumberObject s: newNumberList){
recursive(s);
}
return 0;
}
public static void recursive(NumberObject object){
for (NumberObject x: holdingList){
System.out.println(x);
}
if (!holdingList.contains(object)){
holdingList.add(object);
if (object.hasConnections()){
NumberObject[] newobject = object.getConnections();
for(NumberObject y: newobject){
recursive(y);
}
}
else {
System.out.println(holdingList.size());
return;
}
}
else {
System.out.println(holdingList.size());
return;
}
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int numRows = 0;
int numCols = 0;
String[] firstLine = reader.readLine().split("\\s+");
numRows = Integer.parseInt(firstLine[0]);
numCols = Integer.parseInt(firstLine[1]);
int[][] grid = new int[numRows][numCols];
for (int row = 0; row < numRows; row++) {
String[] inputRow = reader.readLine().split("\\s+");
for (int col = 0; col < numCols; col++) {
grid[row][col] = Integer.parseInt(inputRow[col]);
}
}
int length = longestSequence(grid);
System.out.println(length);
}
}
class NumberObject {
int[] id;
int value;
NumberObject[] connections;
//Constructor
public NumberObject(int[] id, int value){
this.id = id;
this.value = value;
}
//print statement
public String toString(){
return ("NumberOject: Id = " + id + "\nValue = " + value);
}
//Check if it has connections
public boolean hasConnections(){
if (connections == null){
return false;
}
else if (connections.length != 0){
return true;
}
else
return false;
}
//Return the connections it has
public NumberObject[] getConnections(){
return connections;
}
}
Ideally, the image displays what I want to happen.
Instead, all the old branching connections remain on holdingList.
it should be noted paths can branch off to more than two other objects.
Instead of storing the list in a field, you could just pass an instance of a copy of your list to the function as an argument. So the signature of your function recursive would look like:
public static void recursive(NumberObject object, List<NumberObject> visited)
To hide this implementation detail, I recommend writing two functions, whereby the second function just passes an empty list to the other one.
However, I'd choose a different approach since yours acquires as many new lists as entries are in your tree. In the following implementation, you only have one list per "tree end". Moreover, just like in the previous suggestion, this keeps your class stateless.
static List<NumberObject> findLongestPath(NumberObject currentNode) {
if (currentNode.getConnectedNodes().isEmpty()) {
List<NumberObject> result = new ArrayList<>();
result.add(currentNode);
return result;
}
List<NumberObject> longestPath = currentNode.getConnectedNodes().stream()
.map(PathFinder::findLongestPath)
.max(Comparator.comparing(List::size))
.get();
longestPath.add(currentNode);
return longestPath;
}
I have two ArrayLists.
List of dates
List of respective data.
Both are synchronized. I sometimes have more than one data on a same date. I need to create two lists: unique dates and the data (averaged) respectively. So far, I have tried the following methods
int i = 1;
for(int it =0; it < predatetime.size() - 1; it++){
//Compare each element with the next one
if(predatetime.get(it+1) == predatetime.get(it)){
i++;
weight = preweight.get(it+1) + weight;
//If equal, add weights and increment a divisor for averaging
}
else { //if not equal, add to the new lists
if(it == predatetime.size() - 2){ //if the last element is not equal to its previous one, just add it to the list
newDateTime.add(predatetime.get(it+1));
newWeight.add(preweight.get(it+1));
break;
}
weight = weight / i;
newDateTime.add(predatetime.get(it));
newWeight.add(weight);
weight = preweight.get(it+1); //re-initialize variables
i = 1;
}
if(it == predatetime.size() - 2){
weight = weight / i;
newDateTime.add(predatetime.get(it));
newWeight.add(weight);
}
}
There are a lot of problems with this code.
If the list has only one element, it fails. (I know I can write 2 more lines to care of this). Is there a better way to do this?
I know there are similar questions on this website, but still I'm unable to resolve the problem.
This is the full solution
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
public class CustomList {
public static void main(String[] args) {
ArrayList<String> date = new ArrayList<>();
date.add("1");
date.add("2");
date.add("2");
date.add("3");
System.out.println(date);
ArrayList<Integer> value = new ArrayList<>();
value.add(1);
value.add(2);
value.add(4);
value.add(3);
System.out.println(value);
new MyCls().createList(date, value);
}
}
class MyCls {
ArrayList uniqueDate = new ArrayList<String>();
ArrayList averageValue = new ArrayList<Integer>();
LinkedHashMap store = new LinkedHashMap<String, CountEntry>();
class CountEntry {
int value;
int count;
CountEntry() {
}
CountEntry(int v, int c) {
value = v;
count = c;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
public void createList(ArrayList<String> date, ArrayList<Integer> value) {
for (int i = 0; i < date.size(); i++) {
CountEntry tmp = (CountEntry) store.get(date.get(i));
if (tmp == null) {
store.put(date.get(i), new CountEntry(value.get(i), 1));
} else {
int tmpVal = tmp.getValue();
int tmpCount = tmp.getCount();
store.put(date.get(i), new CountEntry(value.get(i) + tmpVal, ++tmpCount));
}
}
ArrayList<String> uniqueDate = new ArrayList<String>(store.keySet());
ArrayList<CountEntry> tempAvgList = new ArrayList<CountEntry>(store.values());
for (CountEntry ce : tempAvgList) {
averageValue.add(ce.getValue() / ce.getCount());
}
System.out.println("Output");
System.out.println(uniqueDate);
System.out.println(averageValue);
}
}
/*
OUTPUT Snap:
[1, 2, 2, 3]
[1, 2, 4, 3]
Output
[1, 2, 3]
[1, 3, 3]
*/
If you try to make your list elements unique why you not try to convert the list to set collection
Set<Foo> foo = new HashSet<Foo>(myList);
Why not create a Map instead with the dates as the key and have the value as a list. This will allow you to keep the dates unique, at the same allow you to have your data as a list.
Map<String, ArrayList<myData>> myMap = new HashMap<String, ArrayList<myData>>();
Then you can just find if your key exists, if it does add it to the array list by using the key to identify the correct list. If it doesnt exist it, add it to the map
Thanks to #Rambler and #JulianGurung, I created a HashMap and it works
HashMap<Integer, Float> hm = new HashMap<Integer,Float>();
int occurance = 0;
float weight = 0;
hm.put(predatetime.get(0), 0f); //initialize with the first value
for(Map.Entry m : hm.entrySet()){
for( int it = 0; it < predatetime.size(); it++){
if(m.getKey() == predatetime.get(it)){
weight = (Float) m.getValue() + preweight.get(it); //Sum all the same data in order to avg later
hm.put(predatetime.get(it), weight);
occurance++;
}
else{ //if it is not equal, add the new element to the map
hm.put(predatetime.get(it), preweight.get(it));
}
}
weight = weight / occurance;
hm.put((Integer) m.getKey(), weight);
weight = 0;
occurance = 0;
}
I have the following map: Map<Integer,String[]> map = new HashMap<Integer,String[]>();
The keys are integers and the values are arrays (could also be replaced by lists).
Now, I would like to get all possible combinations of the values among the keys. For example, let's say the map contains the following entries:
key 1: "test1", "stackoverflow"
key 2: "test2", "wow"
key 3: "new"
The combinations consists of
("test1","test2","new")
("test1","wow","new")
("stackoverflow", "test2", "new")
("stackoverflow", "wow", "new")
For this I imagine a method boolean hasNext() which returns true if there is a next pair and a second method which just returns the next set of values (if any).
How can this be done? The map could also be replaced by an other data structure.
The algorithm is essentially almost the same as the increment algorithm for decimal numbers ("x -> x+1").
Here the iterator class:
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeSet;
public class CombinationsIterator implements Iterator<String[]> {
// Immutable fields
private final int combinationLength;
private final String[][] values;
private final int[] maxIndexes;
// Mutable fields
private final int[] currentIndexes;
private boolean hasNext;
public CombinationsIterator(final Map<Integer,String[]> map) {
combinationLength = map.size();
values = new String[combinationLength][];
maxIndexes = new int[combinationLength];
currentIndexes = new int[combinationLength];
if (combinationLength == 0) {
hasNext = false;
return;
}
hasNext = true;
// Reorganize the map to array.
// Map is not actually needed and would unnecessarily complicate the algorithm.
int valuesIndex = 0;
for (final int key : new TreeSet<>(map.keySet())) {
values[valuesIndex++] = map.get(key);
}
// Fill in the arrays of max indexes and current indexes.
for (int i = 0; i < combinationLength; ++i) {
if (values[i].length == 0) {
// Set hasNext to false if at least one of the value-arrays is empty.
// Stop the loop as the behavior of the iterator is already defined in this case:
// the iterator will just return no combinations.
hasNext = false;
return;
}
maxIndexes[i] = values[i].length - 1;
currentIndexes[i] = 0;
}
}
#Override
public boolean hasNext() {
return hasNext;
}
#Override
public String[] next() {
if (!hasNext) {
throw new NoSuchElementException("No more combinations are available");
}
final String[] combination = getCombinationByCurrentIndexes();
nextIndexesCombination();
return combination;
}
private String[] getCombinationByCurrentIndexes() {
final String[] combination = new String[combinationLength];
for (int i = 0; i < combinationLength; ++i) {
combination[i] = values[i][currentIndexes[i]];
}
return combination;
}
private void nextIndexesCombination() {
// A slightly modified "increment number by one" algorithm.
// This loop seems more natural, but it would return combinations in a different order than in your example:
// for (int i = 0; i < combinationLength; ++i) {
// This loop returns combinations in the order which matches your example:
for (int i = combinationLength - 1; i >= 0; --i) {
if (currentIndexes[i] < maxIndexes[i]) {
// Increment the current index
++currentIndexes[i];
return;
} else {
// Current index at max:
// reset it to zero and "carry" to the next index
currentIndexes[i] = 0;
}
}
// If we are here, then all current indexes are at max, and there are no more combinations
hasNext = false;
}
#Override
public void remove() {
throw new UnsupportedOperationException("Remove operation is not supported");
}
}
Here the sample usage:
final Map<Integer,String[]> map = new HashMap<Integer,String[]>();
map.put(1, new String[]{"test1", "stackoverflow"});
map.put(2, new String[]{"test2", "wow"});
map.put(3, new String[]{"new"});
final CombinationsIterator iterator = new CombinationsIterator(map);
while (iterator.hasNext()) {
System.out.println(
org.apache.commons.lang3.ArrayUtils.toString(iterator.next())
);
}
It prints exactly what's specified in your example.
P.S. The map is actually not needed; it could be replaced by a simple array of arrays (or list of lists). The constructor would then get a bit simpler:
public CombinationsIterator(final String[][] array) {
combinationLength = array.length;
values = array;
// ...
// Reorganize the map to array - THIS CAN BE REMOVED.
I took this as a challenge to see whether the new Java 8 APIs help with these kind of problems. So here's my solution for the problem:
public class CombinatorIterator implements Iterator<Collection<String>> {
private final String[][] arrays;
private final int[] indices;
private final int total;
private int counter;
public CombinatorIterator(Collection<String[]> input) {
arrays = input.toArray(new String[input.size()][]);
indices = new int[arrays.length];
total = Arrays.stream(arrays).mapToInt(arr -> arr.length)
.reduce((x, y) -> x * y).orElse(0);
counter = 0;
}
#Override
public boolean hasNext() {
return counter < total;
}
#Override
public Collection<String> next() {
List<String> nextValue = IntStream.range(0, arrays.length)
.mapToObj(i -> arrays[i][indices[i]]).collect(Collectors.toList());
//rolling carry over the indices
for (int j = 0;
j < arrays.length && ++indices[j] == arrays[j].length; j++) {
indices[j] = 0;
}
counter++;
return nextValue;
}
}
Note that I don't use a map as an input as the map keys actually don't play any role here. You can use map.values() though to pass in the input for the iterator. With the following test code:
List<String[]> input = Arrays.asList(
new String[] {"such", "nice", "question"},
new String[] {"much", "iterator"},
new String[] {"very", "wow"}
);
Iterator<Collection<String>> it = new CombinatorIterator(input);
it.forEachRemaining(System.out::println);
the output will be:
[such, much, very]
[nice, much, very]
[question, much, very]
[such, iterator, very]
[nice, iterator, very]
[question, iterator, very]
[such, much, wow]
[nice, much, wow]
[question, much, wow]
[such, iterator, wow]
[nice, iterator, wow]
[question, iterator, wow]
Intro
My code to do a custom sort by using Comparable is not work the way I want it to. I'm basically taking an Array of directories and sorting them by:
First number of directories, the fewer comes first.
If it's a tie alphabetically.
The problem
An example of an input you be:
["/", "/usr/", "/usr/local/", "/usr/local/bin/", "/games/",
"/games/snake/", "/homework/", "/temp/downloads/" ]
Which should return this:
["/", "/games/", "/homework/", "/usr/", "/games/snake/",
"/temp/downloads/", "/usr/local/", "/usr/local/bin/" ]
But for some reason my code is return this:
["/", "/usr/", "/games/", "/homework/", "/usr/local/",
"/games/snake/", "/usr/local/bin/", "/temp/downloads/" ]
My code [edited with comments]
import java.util.*;
public class Dirsort { public String[] sort(String[] dirs) {
//Creates Array list containing Sort object
ArrayList<Sort> mySort = new ArrayList<Sort>();
//Loop that gets the 3 needed values for sorting
for (String d: dirs){
String [] l = d.split("/");//String array for alphabetical comparison
int di = d.length();//Length of array for sorting by number of directories
mySort.add(new Sort(di,l,d));//adds Sort object to arraylist (note d (the entire directory) is needed for the toString)
}
Collections.sort(mySort);//sorts according to compareTo
String [] ans = new String [mySort.size()];//Creates a new string array that will be returned
int count = 0;//to keep track of where we are in the loop for appending
for (Sort s: mySort){
ans[count] = s.toString();
count++;
}
return ans;
}
class Sort implements Comparable<Sort>{
private int d;//number of directories
private String [] arr;//array of strings of names of directories
private String dir;//full directory as string for toString
//Constructor
public Sort(int myD, String [] myArr, String myDir){
d = myD;
arr = myArr;
dir = myDir;
}
//toString
public String toString(){
return dir;
}
#Override
public int compareTo(Sort arg0) {
// TODO Auto-generated method stub
//If they are the same return 0
if (this.equals(arg0)){
return 0;
}
//if the directories are empty
if("/".equals(arg0.dir)){
return 1;
}
if ("/".equals(this.dir)){
return -1;
}
//If they are not the same length the shorter one comes first
if (this.d != arg0.d){
return this.d - arg0.d;
}
//If they are the same length, compare them alphabetically
else{
for (int i = 0; i < arg0.d; i++){
if (!this.arr[i].equals(arg0.arr[i])){
return this.arr[i].compareTo(arg0.arr[i]);
}
}
}
return 0;
}
}
}
The bug is here:
for (String d: dirs){
String [] l = d.split("/");
int di = d.length(); // <- here
mySort.add(new Sort(di,l,d));
}
Because there you are comparing the length of the entire directory String, not the number of 'folders' in the directory. That's why "/usr/" comes before "/homework/", for example, because:
"/usr/".length() == 5
"/homework/".length() == 10
I believe what you wanted was this, using the length of the split:
int di = l.length;
Then the output is:
/
/games/
/homework/
/usr/
/games/snake/
/temp/downloads/
/usr/local/
/usr/local/bin/
There's another small bug though (possibly), which is that calling split on a String that starts with the delimiter will result in an empty String at the beginning.
IE:
"/usr/".split("/") == { "", "usr" }
So you might want to do something about that. Though here it means that all of them start with the empty String so it doesn't end up with an effect on the way you're doing the comparison.
And as a side note, it's also true what #JBNizet is suggesting that giving your variables more meaningful names helps a lot here. fullDir.length() and splitDir.length would have made this much easier to spot (and it may have never happened in the first place).
Here's a fixed version of your code, which handles the case where both directories are "/", which removes the unnecessary, and incorrectly passed length of the parts array, and which uses more meaningful variable names:
public class Dirsort {
public static void main(String[] args) {
String[] input = new String[] {
"/",
"/usr/",
"/usr/local/",
"/usr/local/bin/",
"/games/",
"/games/snake/",
"/homework/",
"/temp/downloads/"
};
String[] result = new Dirsort().sort(input);
System.out.println("result = " + Arrays.toString(result));
}
public String[] sort(String[] dirs) {
ArrayList<Sort> sorts = new ArrayList<Sort>();
for (String dir : dirs) {
String[] parts = dir.split("/");
sorts.add(new Sort(parts, dir));
}
Collections.sort(sorts);
String[] result = new String[sorts.size()];
int count = 0;
for (Sort sort: sorts) {
result[count] = sort.toString();
count++;
}
return result;
}
class Sort implements Comparable<Sort> {
private String[] parts;
private String dir;
public Sort(String[] parts, String dir) {
this.parts = parts;
this.dir = dir;
}
public String toString(){
return dir;
}
#Override
public int compareTo(Sort other) {
if (this.equals(other)){
return 0;
}
if("/".equals(other.dir) && "/".equals(dir)) {
return 0;
}
if("/".equals(other.dir)){
return 1;
}
if ("/".equals(this.dir)){
return -1;
}
if (this.parts.length != other.parts.length){
return this.parts.length - other.parts.length;
}
else {
for (int i = 0; i < other.parts.length; i++){
if (!this.parts[i].equals(other.parts[i])){
return this.parts[i].compareTo(other.parts[i]);
}
}
}
return 0;
}
}
}
I spotted the problem by simply using my debugger and make it display the value of all the variables.
public class Disort
{
public static String[] sort(String[] dirs)
{
ArrayList<Path> mySort = new ArrayList<Path>();
Path pathDir;
for(String dir : dirs){
pathDir = Paths.get(dir);
// check if directory exists
if(Files.isDirectory(pathDir)){
mySort.add(pathDir);
}
}
// sort the ArrayList according a personalized comparator
Collections.sort(mySort, new Comparator<Path>(){
#Override
public int compare(Path o1, Path o2)
{
if(o1.getNameCount() < o2.getNameCount()){
return -1;
}
else if(o1.getNameCount() > o2.getNameCount()){
return 1;
}
else{
return o1.compareTo(o2);
}
}
});
// to return a String[] but it will better to return a ArrayList<Path>
String[] result = new String[mySort.size()];
for(int i = 0; i < result.length; i++){
result[i] = mySort.get(i).toString();
}
return result;
}
}
I ran into a bind whereby I had to sort the data read from the phones PIM. In doing this I lost the other to which each contact field was referenced to the telephone number because I made use of 2 separate vectors as illustrated below
Before sorting
Nna - +445535533
Ex - +373773737
Ab - +234575757
After sorting.(Which shouldn't be)
Ab - +445535533
Ex - +373773737
Nna - +234575757
This gives an undesired behavior since the sort removes the index to index pointer of the vectors and a selected name (in a Multiple list Box) will get a wrong number.
Alternatively,
I used a hashtable, with the intention of using the names as keys and numbers as the values.
But this pairing means duplicate names being used as keys will not be allowed. Thus I made it a i.e the phone number as keys instead.
I don't want to sound like a cry baby so I stop here for a while and so you the code with a hope u guys would understand it
MY QUESTION
1. Is there a better way/algorithm to implement this?
2. How do I implement the getSelectedItems() in such a ways that it grabs the numbers of the selected indexes of a MULTIPLE CHOICE LIST from a hashTable
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import javax.microedition.lcdui.List;
import javax.microedition.pim.Contact;
import javax.microedition.pim.ContactList;
import javax.microedition.pim.PIM;
import javax.microedition.pim.PIMException;
/**
*
* #author nnanna
*/
public class LoadContacts implements Operation {
private boolean available;
private Vector telNames = new Vector();
Vector telNumbers = new Vector();
Hashtable Listcontact = new Hashtable();
private String[] names;
public Vector getTelNames() {
return telNames;
}
public Hashtable getListcontact() {
return Listcontact;
}
public void execute() {
try {
// go through all the lists
String[] allContactLists = PIM.getInstance().listPIMLists(PIM.CONTACT_LIST);
if (allContactLists.length != 0) {
for (int i = 0; i < allContactLists.length; i++) {
System.out.println(allContactLists[i]);
System.out.println(allContactLists.length);
loadNames(allContactLists[i]);
System.out.println("Execute()");
}
} else {
available = false;
}
} catch (PIMException e) {
available = false;
} catch (SecurityException e) {
available = false;
}
}
private void loadNames(String name) throws PIMException, SecurityException {
ContactList contactList = null;
try {
contactList = (ContactList) PIM.getInstance().openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY, name);
// First check that the fields we are interested in are supported(MODULARIZE)
if (contactList.isSupportedField(Contact.FORMATTED_NAME) && contactList.isSupportedField(Contact.TEL)) {
Enumeration items = contactList.items();
Hashtable temp = new Hashtable();
while (items.hasMoreElements()) {
Contact contact = (Contact) items.nextElement();
int telCount = contact.countValues(Contact.TEL);
int nameCount = contact.countValues(Contact.FORMATTED_NAME);
if (telCount > 0 && nameCount > 0) {
String contactName = contact.getString(Contact.FORMATTED_NAME, 0);
// go through all the phone availableContacts
for (int i = 0; i < telCount; i++) {
System.out.println("Read Telno");
int telAttributes = contact.getAttributes(Contact.TEL, i);
String telNumber = contact.getString(Contact.TEL, i);
Listcontact.put(telNumber, contactName);
temp.put(contactName, telNumber);
}
names = getSortedList();
// Listcontact = temp;
System.out.println(temp + "-------");
System.out.println(Listcontact + "*******");
shortenName(contactName, 20);
}
available = true;
}
} else {
available = false;
}
} finally {
// always close it
if (contactList != null) {
contactList.close();
}
}
}
private void shortenName(String name, int length) {
if (name.length() > length) {
name = name.substring(0, 17) + "...";
}
}
public Vector getSelectedItems(List lbx) {
boolean[] arrSel = new boolean[lbx.size()];
Vector selectedNumbers = new Vector();
int selected = lbx.getSelectedFlags(arrSel);
String selectedString;
String result = "";
for (int i = 0; i < arrSel.length; i++) {
if (arrSel[i]) {
selectedString = lbx.getString(lbx.getSelectedFlags(arrSel));
result = result + " " + i;
System.out.println(Listcontact.get(selectedString));
// System.out.println(telNumbers.elementAt(i));
}
}
return selectedNumbers;
}
private String[] sortResults(String data[]) {
RecordSorter sorter = new RecordSorter();
boolean changed = true;
while (changed) {
changed = false;
for (int j = 0; j < (data.length - 1); j++) {
String a = data[j], b = data[j + 1];
if (a != null && b != null) {
int order = sorter.compare(a.getBytes(), b.getBytes());
if (order == RecordSorter.FOLLOWS) {
changed = true;
data[j] = b;
data[j + 1] = a;
}
}
}
}
return data;
}
public String[] getNames() {
return names;
}
Vector elements = new Vector();
private String[] getValueArray(Hashtable value) {
System.out.println(Listcontact + " c");
Enumeration e = value.elements();
while (e.hasMoreElements()) {
elements.addElement(e.nextElement());
}
String[] elementsArray = new String[elements.size()];
elements.copyInto(elementsArray);
elements.removeAllElements();
System.out.println(elementsArray + " k");
return elementsArray;
}
public void getDuplicates(Vector realValue) {
Vector duplicate = new Vector();
Enumeration e = realValue.elements();
for (int i = 0; e.hasMoreElements(); i++) {
if (duplicate.isEmpty() || !duplicate.elementAt(i).equals(e.nextElement())) {
break;
} else {
duplicate.addElement(e.nextElement());
}
}
}
public String[] getSortedList() {
return sortResults(getValueArray(Listcontact));
}
}
Let me reiterate you requirement: You want a method that will sort the contacts read from native phonebook, then alphabetically sort them on name.
Following is the approach,
Replace the vectors and hash-tables in your code with a single vector, say contactListVector, containing elements of type ContactItem, don't worry this class is explained below. Fundamentally the contact's name and number(s) are linked together in a ContactItem, hence you do not have to worry about there mappings which reduces the usage of redundant data structures.
class ContactItem {
private String name;
private String tnumber; //this can also be a data structure
//for storing multiple numbers
ContactItem( String name, String tnumber) {
this.name = name;
this.tnumber = tnumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTnumber() {
return tnumber;
}
public void setTnumber(String tnumber) {
this.tnumber = tnumber;
}
}
You can reuse the sorting algorithm on contactListVector by comparing the member variable ContactItem.name of the vector element. Also you can deploy different sorts on member variables numbers and/or names. Also there are lots of libraries for JavaME available that have better sorting algorithm's implemented if need be use them.
I would recommend you to perform the sorting once on the contactListVector elements at the end of your method loadNames(...) maybe in the finally block triggered by some boolean variable. The current sorting call in each iteration on items enumeration is expensive and time consuming.
Also you can serialize / deserialize the ContactItem thus persist your contact list.
Let me know if you need detailed explanation.
What about inserting the contact name and numbers inside a recordStore , so you can later make a sort by creating a class which implements RecordComparator.
This statement in your code makes no sense:
selectedString = lbx.getString(lbx.getSelectedFlags(arrSel))
Per lcdui List API documentation above will return the string located at the index equal to the number of selected elements why would you need that?
If you need to output selected text for debugging purposes, use lbx.getString(i) instead.
To implement the getSelectedItems() in such a ways that it grabs the numbers of the selected indexes of a MULTIPLE CHOICE LIST do about as follows:
public Vector getSelectedItems(List lbx) {
boolean[] arrSel = new boolean[lbx.size()];
Vector selectedNumbers = new Vector();
int selected = lbx.getSelectedFlags(arrSel);
System.out.println("selected: [" + selected + "] elements in list");
String selectedString;
String result = "";
for (int i = 0; i < arrSel.length; i++) {
if (arrSel[i]) {
// here, i is the selected index
selectedNumbers.addElement(new Integer(i)); // add i to result
String selectedString = lbx.getString(i);
System.out.println("selected [" + selectedString
+ "] text at index: [" + i + "]");
}
}
return selectedNumbers;
}
As for sorting needs, just drop the HashTable and use Vector of properly designed objects instead as suggested in another answer - with your own sorting algorithm or one from some 3rd party J2ME library.
I would suggest you to have Contact class with name and Vector of numbers. And instead of sorting names array sort the array of contacts.