Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I have a flat file directory structure stored in a csv file which contains
id; parentId; name; type; size; classification; checksum;
1;3;file1;file;10;Secret;42;
2; ;folder2;directory; ; ; ;
3;11;folder3;directory; ; ; ;
4;2;file4;file;40;Secret;42;
5;3;file5;file;50;Public;42;
6;3;file6;file;60;Secret;42;
7;3;file7;file;70;Public;42;
8;10;file8;file;80;Secret;42;
9;10;file9;file;90;Top secret;42;
10;11;folder10;directory; ; ; ;
11;2;folder11;directory; ; ; ;
we need to arrange the structure as tree hierarchy by using id and parent id. I am able to do it manually where
it showed in the figure. But when it comes to coding i am not able to find exact solution using correct data structure.
the output should be in the form of root folder to base folder or files when printing the folder it should print the
size of the files and folders in that folder. An appropriate java or python solution will be helpful
name = folder2, type = Directory, size = 400
name = file4, type = File, size = 40, classification = Secret, checksum = 42
name = folder11, type = Directory, size = 360
name = folder10, type = Directory, size = 170
name = file8, type = File, size = 80, classification = Secret, checksum = 42
name = file9, type = File, size = 90, classification = Top secret, checksum = 42
name = folder3, type = Directory, size = 190
name = file1, type = File, size = 10, classification = Secret, checksum = 42
name = file5, type = File, size = 50, classification = Public, checksum = 42
name = file6, type = File, size = 60, classification = Secret, checksum = 42
name = file7, type = File, size = 70, classification = Public, checksum = 42
Here is a very basic Java implementation.
HashMap<String, Set<String>> nodes = new HashMap<>();
File file = new File("E:/test.txt");
try (Stream<String> stream = Files.lines(file.toPath())) {
stream.forEach((line) -> {
String parts[] = line.split(";");
nodes.putIfAbsent(parts[0], new HashSet<>());
nodes.putIfAbsent(parts[1], new HashSet<>());
nodes.get(parts[1]).add(parts[0]);
});
} catch (Exception e) {
e.printStackTrace();
}
The above code maps children files to their parents. This is a lazy method of implementing a tree wherein you know about all of the nodes and their children, which is all information presented in the flat file directory structure. It streams the file line by line; at each line it takes the node id and parent id, creates nodes for either of them if they don't exist before adding the node id to the parent id children set.
The node HashMap looks like this at this stage:
{ =[2], 11=[3, 10], 1=[], 2=[11, 4], 3=[1, 5, 6, 7], 4=[], 5=[], 6=[], 7=[], 8=[], 9=[], 10=[8, 9]}
If you are happy with treating the " " String as the root, you can begin recursively traversing through the list by retrieving the root nodes.get(" ");, otherwise you will need to define how you retrieve the root node.
One potential way of adopting this approach while maintaining the information is to treat each File as a Node.
public static void main (String args[]) {
HashMap<Node, Set<Node>> nodes = new HashMap<>();
File file = new File("E:/test.txt");
try (Stream<String> stream = Files.lines(file.toPath())) {
stream.forEach((line) -> {
String parts[] = line.split(";");
Node node = new Node(parts);
Node parent = new Node(parts[1]);
nodes.putIfAbsent(parent, new HashSet<>());
nodes.putIfAbsent(node, new HashSet<>());
nodes.get(parent).add(node);
});
} catch (Exception e) {
e.printStackTrace();
}
calculateSize(nodes, new Node(" "));
traverse(nodes, new Node(" "));
}
public static void traverse(HashMap<Node, Set<Node>> nodes, Node root) {
System.out.println(root);
for (Node child : nodes.get(root)) {
traverse(nodes, child);
}
}
public static int calculateSize(HashMap<Node, Set<Node>> nodes, Node root) {
int size = root.getSize();
for (Node child : nodes.get(root)) {
size += calculateSize(nodes, child);
}
root.setSize(size);
return size;
}
public static class Node {
private String id = " ";
private String type= " ";
private String name = " ";
private int size = 0;
private String classification = " ";
private int checksum = 0;
public Node (String[] parts) {
this.id = parts[0];
this.name = parts[2];
this.type = parts[3];
this.classification = parts[5];
if (this.type.equals("file")) {
this.size = Integer.parseInt(parts[4]);
this.checksum = Integer.valueOf(parts[6]);
}
}
public Node (String id) {
this.id = id;
}
#Override
public String toString() {
if (name.equals(" ")) return "Root";
String toString = (name.equals(" ")) ? "" : String.format("name = %s", name);
toString += (type.equals(" ")) ? "" : String.format(", type = %s", type);
toString += String.format(", size = %d", size);
toString += (classification.equals(" ")) ? "" : String.format(", classification = %s", classification);
toString += String.format(", checksum = %d", checksum);
return toString;
}
#Override
public boolean equals(Object o) {
if (o instanceof Node) {
return ((Node) o).id.equals(this.id);
} else if (o instanceof String) {
return o.equals(this.id);
}
return false;
}
#Override
public int hashCode() {
return this.id.hashCode();
}
public int getSize() { return size; }
public void setSize(int size) { this.size = size; }
}
The recursive search allows you to traverse through the nodes and print them to console. Note I've overriden the equals, hashCode and toString methods. The equals and hashCode allow you to avoid duplicates and ensure node association is retained.
If you're wanting to sum the files up, you can, as I have suggested, use recursion to calculate the sum for a specific directory and all it's children.
Your question is extremely unclear as to what you want. Please be more clear next time.
As a side note: You're diagram is incorrect. 7 is only a sub-directory of 3, not 10.
You could make 2 classes File and Folder where you can override the __str__() method in those classes to what you need.
There was very similar question, asked some time ago :
similar question
What I have is this :
sorted WRONG as we can see.
Bean file:
init(){
root = new DefaultTreeNode("Root", null);
createFolderTree(folderManager.getSiteFolderId(),root);
Collections.sort(root, new TreeNodeComparator());
}
public void createFolderTree(String id, TreeNode root) {
List<CmisObject> childrenList = new ArrayList<>();
ItemIterable<CmisObject> children = folderManager.getFolderChildren(id);
Iterator<CmisObject> iterator = children.iterator();
while (iterator.hasNext()) {
childrenList.add(iterator.next());
}
Collections.sort(childrenList, new TreeNodeComparator(SortOrder.ASCENDING));
if (children.getPageNumItems() > 0) {
for (CmisObject o : childrenList) {
if (o.getBaseTypeId().toString() != "CMIS_DOCUMENT") {
TreeNode newNode = new DefaultTreeNode(o, root);
createFolderTree(o.getId(), newNode);
}
}
}
}
Comparator
public class TreeNodeComparator implements Comparator<CmisObject> {
private SortOrder sortOrder;
private static final long serialVersionUID = 1L;
public TreeNodeComparator(SortOrder sortOrder) {
this.sortOrder = sortOrder;
}
#Override
public int compare(CmisObject n1, CmisObject n2) {
try {
Object value1 = n1.getName();
Object value2 = n2.getName();
String simple = "< a< ą< b < c < č < d < e < ę < i < į < y< j < k < l < s < š< t< u < ų< v < z < ž ";
RuleBasedCollator lt_LTCollator = new RuleBasedCollator(simple);
Collator lithuanianCollator = Collator.getInstance(new Locale("lt_LT"));
lt_LTCollator.setStrength(Collator.PRIMARY);
int value = lt_LTCollator.compare(value1.toString(), value2.toString());
return SortOrder.ASCENDING.equals(sortOrder) ? value : -1 * value;
} catch (Exception e) {
throw new RuntimeException();
}
}
public SortOrder getSortOrder() {
return sortOrder;
}
public void setSortOrder(SortOrder sortOrder) {
this.sortOrder = sortOrder;
}
}
Also I tried to do it in a same way as in a link posted before, but I get that my sort is not applicable for this..
Maybe I am mising something very simple, but this starts to annoy me.
Any help would be nice,
and.. I know that root is not a List :)
But.. is there a way, to do sorting like this.
EDIT:
So I updated my code a bit, comparator working as it Should, I tested it on
List<String> aa = Arrays.asList("ž", "a", "aa", "c", "ąąą", "ęęę", "ąą", "vv", "žžž", "zz", "ėėė");
and output was:
[a, aa, ąą, ąąą, c, ėėė, ęęę, vv, zz, ž, žžž]
thanks in advance.
This is not a JSF specific problem. JSF is merely the model view presenter here. You'd have had exactly the same problem when presenting the already-prepared model to the view in a different way, such as System.out.println().
Let me demonstrate:
List<String> strings = Arrays.asList("zzz", "ąąą", "žžž", "ššš", "aaa", "sss");
Collections.sort(strings);
System.out.println(strings);
[aaa, sss, zzz, ąąą, ššš, žžž]
This sorting is according the English rule. This is the default behavior when you sort without a language-specific collation using the java.text.Collator API.
In below example I'll assume you're interested in Polish collation rules.
List<String> strings = Arrays.asList("zzz", "ąąą", "žžž", "ššš", "aaa", "sss");
Collections.sort(strings, Collator.getInstance(new Locale("pl")));
System.out.println(strings);
[aaa, ąąą, sss, ššš, zzz, žžž]
You'll need to change your TreeNodeComparator to take this into account. As you're in JSF context, consider extracting the locale from UIViewRoot#getLocale().
I have been working on something the past few days that seems to be working as intended, however I am looking for ways to improve it. I have a set of n items, and I need to put together groups of these items that MUST meet ALL of the following requirements:
2 items from Category A
2 items from Category B
2 items from Category C
2 Items from Category D
1 item from Category E
I am currently using the following recursive method to put my groups together and the isValid() method is being used to determine if the group meets the criteria.
void getGroups(String[] arr, int len, int startPosition, String[] result) {
if(len == 0) {
Group group = new Group(Arrays.asList(result));
if(group.isValid()) {
validGroups.add(group);
group.printGroup();
}
return;
}
for(int i = startPosition; i <= arr.length - len; i++) {
result[result.length - len] = arr[i];
getGroups(arr, len - 1, i + 1, result);
}
}
I am able to see valid results get printed as the program runs, however the original size of items that I am working with can be well over 100 items. This means there is a very large number of total possible groups that will be iterated through and a lot of times the program never actually completes.
I know that there are currently a bunch of wasted iterations, for example if at some point I detect a group is invalid because it has 3 items from Category A, I should be able to move on. I am not sure if my current method with a few tweaks is the best way to go about this, or if I should separate the items into their respective groups first, and then from their put only valid combinations together. Any help would be appreciated. Thanks.
EDIT: I tried to make the method a bit more simpler than my actual method. My actual method takes in an array of Objects that I've created that contain their value along with their category. I guess for the example we can assume that each category is represented by a list of Strings that it contains. The method can be called like:
String[] items = {"test1", "test2", "test3", "test4", "test5", "test6", "test7",
"test8", "test9", "test10", "test11", "test12", "test13",
"test14", "test15", "test16", "test17", "test18"};
getGroups(items, 9, 0, new String[9]);
EDIT2:
List<String> catA = new ArrayList<String>();
catA.add("test1");
catA.add("test2");
catA.add("test3");
catA.add("test4");
List<String> catB = new ArrayList<String>();
catB.add("test5");
catB.add("test6");
catB.add("test7");
catB.add("test8");
List<String> catC = new ArrayList<String>();
catC.add("test9");
catC.add("test10");
catC.add("test11");
catC.add("test12");
List<String> catS = new ArrayList<String>();
catD.add("test13");
catD.add("test14");
catD.add("test15");
catD.add("test16");
List<String> catE = new ArrayList<String>();
catE.add("test17");
catE.add("test18");
Output:
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test14", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test14", "test18"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test16", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test15", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test14", "test15", "test17"}
etc...
This seems to work.
I use a BitPattern iterator I wrote a while ago that walks all n-bit numbers containing just k set bits and uses that to select from your categories.
Note that much of this code is building the test data to reflect your requirements.
I hold a List of Iterables which are the BitPatterns. A list of Iterators which are the currently in-use Iterators from the BitPatterns (they must be renewed every time they complete) and a List of BigIntgers that are the current values to explode into selections from the data.
public class Test {
enum Category {
A(2), B(2), C(2), D(2), E(1);
public final int required;
Category(int required) {
this.required = required;
}
}
private static final Category[] categories = Category.values();
static class Categorised {
final String name;
final Category category;
Categorised(String name, Category category) {
this.name = name;
this.category = category;
}
#Override
public String toString() {
return category.name() + ":" + name;
}
}
static final List<Categorised> data = new ArrayList<>();
static {
data.add(new Categorised("A-1", Category.A));
data.add(new Categorised("A-2", Category.A));
data.add(new Categorised("A-3", Category.A));
data.add(new Categorised("B-1", Category.B));
data.add(new Categorised("B-2", Category.B));
data.add(new Categorised("B-3", Category.B));
data.add(new Categorised("C-1", Category.C));
data.add(new Categorised("C-2", Category.C));
data.add(new Categorised("C-3", Category.C));
data.add(new Categorised("D-1", Category.D));
data.add(new Categorised("D-2", Category.D));
data.add(new Categorised("D-3", Category.D));
data.add(new Categorised("E-1", Category.E));
data.add(new Categorised("E-2", Category.E));
data.add(new Categorised("E-3", Category.E));
}
// Categorise the data.
private Map<Category, List<Categorised>> categorise(List<Categorised> data) {
Map<Category, List<Categorised>> categorised = new EnumMap<>(Category.class);
for (Categorised d : data) {
List<Categorised> existing = categorised.get(d.category);
if (existing == null) {
existing = new ArrayList<>();
categorised.put(d.category, existing);
}
existing.add(d);
}
return categorised;
}
public void test() {
// Categorise the data.
Map<Category, List<Categorised>> categorised = categorise(data);
// Build my lists.
// A source of Iteratprs.
List<BitPattern> is = new ArrayList<>(categories.length);
// The Iterators.
List<Iterator<BigInteger>> its = new ArrayList<>(categories.length);
// The current it patterns to use to select.
List<BigInteger> next = new ArrayList<>(categories.length);
for (Category c : categories) {
int k = c.required;
List<Categorised> from = categorised.get(c);
// ToDo - Make sure there are enough.
int n = from.size();
// Make my iterable.
BitPattern p = new BitPattern(k, n);
is.add(p);
// Gather an Iterator.
Iterator<BigInteger> it = p.iterator();
// Store it.
its.add(it);
// Prime it.
next.add(it.next());
}
// Walk the lists.
boolean stepped;
do {
// Interpret the current numbers.
List<Categorised> candidates = new ArrayList<>();
for ( int c = 0; c < categories.length; c++ ) {
BigInteger b = next.get(c);
List<Categorised> category = categorised.get(categories[c]);
// Step through the bits in the number.
BitSet bs = BitSet.valueOf(b.toByteArray());
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
// Pull those entries from the categorised list.
candidates.add(category.get(i));
}
}
// Print it for now.
System.out.println(candidates);
// Step again.
stepped = step(is, its, next);
} while (stepped);
}
// Take one step.
private boolean step(List<BitPattern> is, List<Iterator<BigInteger>> its, List<BigInteger> next) {
boolean stepped = false;
// Step each one until we make one successful step.
for (int i = 0; i < is.size() && !stepped; i++) {
Iterator<BigInteger> it = its.get(i);
if (it.hasNext()) {
// Done here!
stepped = true;
} else {
// Exhausted - Reset it.
its.set(i, it = is.get(i).iterator());
}
// Pull that one.
next.set(i, it.next());
}
return stepped;
}
public static void main(String args[]) {
new Test().test();
}
}
This is the BitPattern iterator.
/**
* Iterates all bit patterns containing the specified number of bits.
*
* See "Compute the lexicographically next bit permutation"
* http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
*
* #author OldCurmudgeon
*/
public class BitPattern implements Iterable<BigInteger> {
// Useful stuff.
private static final BigInteger ONE = BigInteger.ONE;
private static final BigInteger TWO = ONE.add(ONE);
// How many bits to work with.
private final int bits;
// Value to stop at. 2^max_bits.
private final BigInteger stop;
// Should we invert the output.
private final boolean not;
// All patterns of that many bits up to the specified number of bits - invberting if required.
public BitPattern(int bits, int max, boolean not) {
this.bits = bits;
this.stop = TWO.pow(max);
this.not = not;
}
// All patterns of that many bits up to the specified number of bits.
public BitPattern(int bits, int max) {
this(bits, max, false);
}
#Override
public Iterator<BigInteger> iterator() {
return new BitPatternIterator();
}
/*
* From the link:
*
* Suppose we have a pattern of N bits set to 1 in an integer and
* we want the next permutation of N 1 bits in a lexicographical sense.
*
* For example, if N is 3 and the bit pattern is 00010011, the next patterns would be
* 00010101, 00010110, 00011001,
* 00011010, 00011100, 00100011,
* and so forth.
*
* The following is a fast way to compute the next permutation.
*/
private class BitPatternIterator implements Iterator<BigInteger> {
// Next to deliver - initially 2^n - 1
BigInteger next = TWO.pow(bits).subtract(ONE);
// The last one we delivered.
BigInteger last;
#Override
public boolean hasNext() {
if (next == null) {
// Next one!
// t gets v's least significant 0 bits set to 1
// unsigned int t = v | (v - 1);
BigInteger t = last.or(last.subtract(BigInteger.ONE));
// Silly optimisation.
BigInteger notT = t.not();
// Next set to 1 the most significant bit to change,
// set to 0 the least significant ones, and add the necessary 1 bits.
// w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
// The __builtin_ctz(v) GNU C compiler intrinsic for x86 CPUs returns the number of trailing zeros.
next = t.add(ONE).or(notT.and(notT.negate()).subtract(ONE).shiftRight(last.getLowestSetBit() + 1));
if (next.compareTo(stop) >= 0) {
// Dont go there.
next = null;
}
}
return next != null;
}
#Override
public BigInteger next() {
last = hasNext() ? next : null;
next = null;
return not ? last.not(): last;
}
#Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
#Override
public String toString () {
return next != null ? next.toString(2) : last != null ? last.toString(2): "";
}
}
}
I will not write code but will list a possible approach. I say possible because it will be running and storing all data in memory and is not the best in regard to algorithms. yet, it is an approach where you don't need to eliminate invalid options. I will use an example in order to make things more clear.
suppose you have categories A,B,C. Where K=2 for A,B and K=1 for C.
you also have the input items A1,B1,B2,A2,C1,A3
1- go over the items and divide them according to their category. so you prepare an array/list for each category that has all the items that belong to it.
so now you have arrays:
Category A = [A1,A2,A3] , Category B = [B1,B2] and Category C=[C1]
2- now after preparing the lists, prepare the various legal groups that you can have for picking K items from N items found in that list . here is a link that might help in doing that efficiently: How to iteratively generate k elements subsets from a set of size n in java?
now you have:
first group belonging to category A:
[A1,A2] , [A1,A3], [A2,A3] (3 elements)
second group belonging to category B:
[B1,B2] (1 element)
third group belonging to category C:
[C1] (1 element)
3- now, if you treat each such group as an item, the question transforms to how many different ways are there for picking exactly one element from each group. and that is supposed to be easier to program recursively and will not require eliminating options. and if the number of categories is constant, it will be nested loops over the sets of groups in second point above.
EDIT
the approach works well in eliminating the need to validate bad combinations.
yet, there will still be a problem in regard of time. Here is the code that I made to demonstrate. it makes a list of 100 items. then it does the steps mentioned.
Note that I commented out the code that prints the groups.
The calculation is very fast up to that point. I have added code that prints how many legal choices can be made from each group.
package tester;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
*
* #author
*/
public class Tester {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
//generate 100 random items belonging to categories
Random rand=new Random();
List<Item> items=new ArrayList<>();
int a=1,b=1,c=1,d=1,e=1;
for (int i=0;i<100;i++){
int randomNumber=rand.nextInt(5)+1;
CATEGORY_TYPE categoryType=null;
int num=0;
switch (randomNumber) {
case 1:
categoryType=CATEGORY_TYPE.A;
num=a++;
break;
case 2:
categoryType=CATEGORY_TYPE.B;
num=b++;
break;
case 3:
categoryType=CATEGORY_TYPE.C;
num=c++;
break;
case 4:
categoryType=CATEGORY_TYPE.D;
num=d++;
break;
case 5:
categoryType=CATEGORY_TYPE.E;
num=e++;
break;
}
String dummyData="Item "+categoryType.toString()+num;
Item item=new Item(dummyData,categoryType);
items.add(item);
}
//arrange the items in lists by category
List<Item> categoryAItemsList=new ArrayList<>();
List<Item> categoryBItemsList=new ArrayList<>();
List<Item> categoryCItemsList=new ArrayList<>();
List<Item> categoryDItemsList=new ArrayList<>();
List<Item> categoryEItemsList=new ArrayList<>();
for (Item item:items){
if (item.getCategoryType()==CATEGORY_TYPE.A)
categoryAItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.B)
categoryBItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.C)
categoryCItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.D)
categoryDItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.E)
categoryEItemsList.add(item);
}
//now we want to construct lists of possible groups of choosing from each category
List<Item[]> subsetStoringListA=new ArrayList<>();
List<Item[]> subsetStoringListB=new ArrayList<>();
List<Item[]> subsetStoringListC=new ArrayList<>();
List<Item[]> subsetStoringListD=new ArrayList<>();
List<Item[]> subsetStoringListE=new ArrayList<>();
processSubsets(categoryAItemsList.toArray(new Item[0]),2,subsetStoringListA);
processSubsets(categoryBItemsList.toArray(new Item[0]),2,subsetStoringListB);
processSubsets(categoryCItemsList.toArray(new Item[0]),2,subsetStoringListC);
processSubsets(categoryDItemsList.toArray(new Item[0]),2,subsetStoringListD);
processSubsets(categoryEItemsList.toArray(new Item[0]),1,subsetStoringListE);
System.out.println(" A groups number: "+subsetStoringListA.size());
System.out.println(" B groups number: "+subsetStoringListB.size());
System.out.println(" C groups number: "+subsetStoringListC.size());
System.out.println(" D groups number: "+subsetStoringListD.size());
System.out.println(" E groups number: "+subsetStoringListE.size());
//now we just print all possible combinations of picking a single group from each list.
//the group is an array with valid choices
// for (Item[] subsetA:subsetStoringListA){
// for (Item[] subsetB:subsetStoringListB){
// for (Item[] subsetC:subsetStoringListC){
// for (Item[] subsetD:subsetStoringListD){
// for (Item[] subsetE:subsetStoringListE){
// print(subsetA);
// print(subsetB);
// print(subsetC);
// print(subsetD);
// print(subsetE);
// System.out.println("\n");
// }
//
// }
// }
// }
// }
}
static void print(Item[] arr){
for (Item item:arr)
System.out.print(item.getDumyData()+" ");
}
static void processSubsets(Item[] set, int k,List<Item[]> subsetStoringList) {
Item[] subset = new Item[k];
processLargerSubsets(set, subset, 0, 0,subsetStoringList);
}
static void processLargerSubsets(Item[] set, Item[] subset, int subsetSize, int nextIndex,List<Item[]> subsetStoringList) {
if (subsetSize == subset.length) { //here we have a subset we need to store a copy from it
subsetStoringList.add(Arrays.copyOf(subset, subset.length));
} else {
for (int j = nextIndex; j < set.length; j++) {
subset[subsetSize] = set[j];
processLargerSubsets(set, subset, subsetSize + 1, j + 1,subsetStoringList);
}
}
}
public static enum CATEGORY_TYPE {A,B,C,D,E}
private static class Item{
private CATEGORY_TYPE categoryType;
private String dumyData;
public Item(String dumyData,CATEGORY_TYPE categoryType) {
this.dumyData = dumyData; //maybe bad name but i mean the object can have many other fields etc
this.categoryType = categoryType;
}
/**
* #return the categoryType
*/
public CATEGORY_TYPE getCategoryType() {
return categoryType;
}
/**
* #return the dumyData
*/
public String getDumyData() {
return dumyData;
}
}
}
in a specific run, it gave the following:
A groups number: 210
B groups number: 153
C groups number: 210
D groups number: 210
E groups number: 19
that means , if we had to print all possible choices of a single element (and here an elemnt is an array containing k choices from a category) from each of these, you will have : 210*153*210*210*19 = 26,921,727,000
now listing/printing over 26 billion variations will take time no matter what and I don't see how it will be minimized.
try setting the total items to 20 and uncomment the printing code to see that everything is working correctly. And see if you really need to list the possible combinations. please remember that every combination here is legal and there are no wasted iterations in all the parts of the code.
one final note: I did not treat edge cases like when there are no items in a category to complete K. that you can easily put in the code according to the desired behaviour in that case.
So this seems to be a constraint satisfaction problem. So maybe try backtracking?
I believe the following works, but plug in your own data to guaranteee.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Launch {
public static void main(String[] args) {
// Formulate the constraints.
int[] constraints = { 2, 1, 0, 1 };
// Create all the items.
List<boolean[]> items = new ArrayList<boolean[]>();
boolean[] i1 = { true, false, true, false };
boolean[] i2 = { true, false, false, false };
boolean[] i3 = { false, true, false, true };
boolean[] i4 = { false, false, false, true };
items.add(i1);
items.add(i2);
items.add(i3);
items.add(i4);
// Solve!
backtrack(constraints, items);
}
/**
* Recursively generate possible solutions but backtrack as soon as the constraints fail.
*/
private static void backtrack(int[] constraints, List<boolean[]> items) {
// We start with no items belonging to any categories.
List<List<boolean[]>> memberships = new ArrayList<List<boolean[]>>();
for (int i = 0; i < constraints.length; i++) {
memberships.add(new ArrayList<boolean[]>());
}
backtrack(constraints, items, memberships);
}
/**
* Recursively generate possible solutions but backtrack as soon as the constraints fail.
*/
private static void backtrack(int[] constraints, List<boolean[]> items,
List<List<boolean[]>> memberships) {
if (items.isEmpty() && !acceptable(constraints, memberships)) {
return;
} else if (acceptable(constraints, memberships)) {
display(memberships);
} else {
for (boolean[] item : items) {
int catIdx = 0;
for (boolean belongs : item) {
if (belongs) {
// The item and category were chosen so let's update
// memberships.
List<List<boolean[]>> newMemberships = new ArrayList<List<boolean[]>>();
for (List<boolean[]> old : memberships) {
newMemberships.add(new ArrayList<boolean[]>(old));
}
newMemberships.get(catIdx).add(item);
// We've placed the item so let's remove it from the
// possibilities.
List<boolean[]> newItems = new ArrayList<boolean[]>(
items);
newItems.remove(item);
// Now solve the sub-problem.
backtrack(constraints, newItems, newMemberships);
}
catIdx++;
}
}
}
}
/**
* A nice way to display the membership tables.
*/
private static void display(List<List<boolean[]>> memberships) {
System.out.println("---");
for (List<boolean[]> category : memberships) {
for (boolean[] item : category) {
System.out.print(Arrays.toString(item) + " ");
}
System.out.println();
}
}
/**
* Returns whether or not a list of memberships are accepted by the
* constraints.
*
* #param constraints
* - The number of items required per category.
* #param memberships
* - The current items per category.
*/
private static boolean acceptable(int[] constraints,
List<List<boolean[]>> memberships) {
boolean acceptable = memberships.size() == constraints.length;
for (int i = 0; i < memberships.size(); i++) {
acceptable = acceptable
&& constraints[i] == memberships.get(i).size();
}
return acceptable;
}
}
I have two arraylist name preBusinessList, businessList. In business List I have data from server, and in preBusinessList is the local one. In lists I have id, count value Betterly demonstrate as below
Now I wanted to make a newBusinessList like this
How can I do it in java, please help me to solve this
Then I would use a map to do the merge using id as the key and convert it back to your list of (id,value) pairs
You can use:
Collections.sort(new ArrayList<...>(preBusinessList).addAll(businessList), comparator)
Where comparator is a class that implements Comparator interface (will be responsible for sorting as you wish)
assumming i understood your problem correctly (big if...):
also, i assume each element in the lists is a Pair - as it looks from your data (just a dumb wrapper class that holds 2 integers). if its some other class you'll need to adjust this code.
private Map<Integer,Integer> finalValues = new HashMap<Integer,Integer>();
for (Pair<Integer,Integer> entry : preBusinessList) {
finalValues.put(entry.getFirst(), entry.getSecond());
}
//2nd list overwrites values from 1st (anything not overwritten remains)
for (Pair<Integer,Integer> entry : businessList) {
finalValues.put(entry.getFirst(), entry.getSecond());
}
ArrayList<Pair<Integer,Integer>> finalList = new ArrayList<>();
for (Map.Entry<Integer,Integer> entry : finalValues) {
finalList.add(new Pair(entry.getKey(), entry.getValue());
}
//and now sort the list
Collections.sort(finalList, new Comparator<Pair<Integer,Integer>> {
int compare(Pair<Integer,Integer> a, Pair<Integer,Integer>b) {
return a.getFirst.compareTo(b.getFirst()); //compare by 1st number in pair only
}
});
Assuming something like:
public class Info {
public int id;
public int info;
}
You could merge them on the basis of wanting the keep the one with higher info field as follows:
// Assumes:
// - that the ArrayLists are sorted to have id in order going up
// - no repeated ids in a or in b (but same id can be in both a and b)
ArrayList<Info> merge(ArrayList<Info> a, ArrayList<Info> b) {
int aLength = a.size();
int bLength = b.size();
int ai = 0;
int bi = 0;
ArrayList<Info> result = new ArrayList<Info>();
while ((ai < aLength) && (bi < bLength))
Info aInfo = a.get(ai);
Info bInfo = b.get(bi);
if (aInfo.id == bInfo.id) {
if (aInfo.info >= bInfo.info) result.add(aInfo);
else result.add(bInfo);
ai++;
bi++;
}
else if (aInfo.id < bInfo.id) {
result.add(aInfo);
ai++;
}
else {
result.add(bInfo);
bi++;
}
}
// Add the remaining terms - only one of the loops will actually do anything
for (; ai<aiLength; ai++) {
result.add(a.get(ai));
}
for (; bi<biLength; bi++) {
result.add(b.get(bi));
}
}
Pseudocode :
Iterate over preBusinessList.
Fetch key and see if this key(1,2,3,4,5,6) exists in businesslist
If yes conitnue
Else If no, then add it to businesslist
for(Map.Entry<Integer, Integer> keyValue : preBusinessList.entrySet()) {
if(!businesslist.containsKey(keyValue.getKey())) {
businesslist.put(keyValue.getKey(), keyValue.getValue());
}
}
Updated Answer as per new requirements
boolean ifExists = false;
for(PlaceItems itemPreBusinessList : preBusinessList) {
ifExists = false;
for(PlaceItems itemBusinessList : businessList) {
if(itemBusinessList.businessId == itemPreBusinessList.businessId) {
// Already exists
ifExists = true;
break;
}
}
if(!isExists) {
businessList.add(itemPreBusinessList);
}
}
I have written a generic Partition class (a partition is a division of a set into disjoint subsets, called parts). Internally this is a Map<T,Integer> and a Map<Integer,Set<T>>, where the Integers are the labels of the parts. For example partition.getLabel(T t) gives the label of the part that t is in, and partition.move(T t, Integer label) moves t to the partition labelled by label (internally, it updates both the Maps).
But my method for moving a Collection of objects to a new part does not work. It seems that Set.removeAll() is affecting its argument. I think the problem is something like a ConcurrentModificationException, but I can't work it out. Sorry the code is rather long, but I have marked where the problem is (about half-way down), and the output at the bottom should make it clear what the problem is - at the end the partition is in an illegal state.
import java.util.*;
public class Partition<T> {
private Map<T,Integer> objToLabel = new HashMap<T,Integer>();
private Map<Integer,Set<T>> labelToObjs =
new HashMap<Integer,Set<T>>();
private List<Integer> unusedLabels;
private int size; // = number of elements
public Partition(Collection<T> objects) {
size = objects.size();
unusedLabels = new ArrayList<Integer>();
for (int i = 1; i < size; i++)
unusedLabels.add(i);
// Put all the objects in part 0.
Set<T> part = new HashSet<T>(objects);
for (T t : objects)
objToLabel.put(t,0);
labelToObjs.put(0,part);
}
public Integer getLabel(T t) {
return objToLabel.get(t);
}
public Set<T> getPart(Integer label) {
return labelToObjs.get(label);
}
public Set<T> getPart(T t) {
return getPart(getLabel(t));
}
public Integer newPart(T t) {
// Move t to a new part.
Integer newLabel = unusedLabels.remove(0);
labelToObjs.put(newLabel,new HashSet<T>());
move(t, newLabel);
return newLabel;
}
public Integer newPart(Collection<T> things) {
// Move things to a new part. (This assumes that
// they are all in the same part to start with.)
Integer newLabel = unusedLabels.remove(0);
labelToObjs.put(newLabel,new HashSet<T>());
moveAll(things, newLabel);
return newLabel;
}
public void move(T t, Integer label) {
// Move t to the part "label".
Integer oldLabel = getLabel(t);
getPart(oldLabel).remove(t);
if (getPart(oldLabel).isEmpty()) // if the old part is
labelToObjs.remove(oldLabel); // empty, remove it
getPart(label).add(t);
objToLabel.put(t,label);
}
public void moveAll(Collection<T> things, Integer label) {
// Move all the things from their current part to label.
// (This assumes all the things are in the same part.)
if (things.size()==0) return;
T arbitraryThing = new ArrayList<T>(things).get(0);
Set<T> oldPart = getPart(arbitraryThing);
// THIS IS WHERE IT SEEMS TO GO WRONG //////////////////////////
System.out.println(" oldPart = " + oldPart);
System.out.println(" things = " + things);
System.out.println("Now doing oldPart.removeAll(things) ...");
oldPart.removeAll(things);
System.out.println(" oldPart = " + oldPart);
System.out.println(" things = " + things);
if (oldPart.isEmpty())
labelToObjs.remove(objToLabel.get(arbitraryThing));
for (T t : things)
objToLabel.put(t,label);
getPart(label).addAll(things);
}
public String toString() {
StringBuilder result = new StringBuilder();
result.append("\nPARTITION OF " + size + " ELEMENTS INTO " +
labelToObjs.size() + " PART");
result.append((labelToObjs.size()==1 ? "" : "S") + "\n");
for (Map.Entry<Integer,Set<T>> mapEntry :
labelToObjs.entrySet()) {
result.append("PART " + mapEntry.getKey() + ": ");
result.append(mapEntry.getValue() + "\n");
}
return result.toString();
}
public static void main(String[] args) {
List<String> strings =
Arrays.asList("zero one two three".split(" "));
Partition<String> p = new Partition<String>(strings);
p.newPart(strings.get(3)); // move "three" to a new part
System.out.println(p);
System.out.println("Now moving all of three's part to the " +
"same part as zero.\n");
Collection<String> oldPart = p.getPart(strings.get(3));
//oldPart = Arrays.asList(new String[]{"three"}); // works fine!
p.moveAll(oldPart, p.getLabel(strings.get(0)));
System.out.println(p);
}
}
/* OUTPUT
PARTITION OF 4 ELEMENTS INTO 2 PARTS
PART 0: [two, one, zero]
PART 1: [three]
Now moving all of three's part to the same part as zero.
oldPart = [three]
things = [three]
Now doing oldPart.removeAll(things) ...
oldPart = []
things = []
PARTITION OF 4 ELEMENTS INTO 1 PART
PART 0: [two, one, zero]
*/
Using my debugger I place a breakpoint before the removeAll and I can see (as I suspected) that oldPart and things as the same collection so removing all elements clears that collection.
Your code is extremely confusing but as far as I can work out, oldPart and things are actually the same object. Set.removeAll() certainly doesn't affect its argument unless it is the same object as it's invoked on:
public boolean removeAll(Collection<?> c) {
boolean modified = false;
if (size() > c.size()) {
for (Iterator<?> i = c.iterator(); i.hasNext(); )
modified |= remove(i.next());
} else {
for (Iterator<?> i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {
i.remove();
modified = true;
}
}
}
return modified;
}
Update:
The easy way to eliminate this is to use a copy of things in the moveAll() method. Indeed such a copy already exists.
T arbitraryThing = new ArrayList<T>(things).get(0);
This line creates but then instantly discards a copy of things. So I'd suggest replacing it with:
ArrayList<T> thingsToRemove = new ArrayList<T>(things)
T arbitraryThing = thingsToRemove.get(0);
And in the rest of the method, replace all references to things to thingsToRemove.