I need to join two Lists in Java. I've a list which has a VO that has a name and description. I've another list which has same VO type that has name and Address. The "name" is same. I need to create a List with this VO which has both name, address and description.
The VO structure is
public class PersonDetails{
private String name;
private String description;
private String address;
//getters and setters
}
Can someone please suggest me the best way to implement it?
It depends:
If the lists contain both exactly the same data, you can sort them both by name, iterate over them en set the missing property.
If not, I would put the first list in a Map, with the name as key. Then iterate over the second list, look in the map for the VO and set the value.
After that, just get all the value's out of the map again as a List.
public List<Vo> merge(List<Vo> list1, List<Vo> list2) {
Map<String, Vo> tempMap = new HashMap<String, Vo>();
for (Vo v : list1) {
tempMap.put(v.name, v);
}
for (Vo vv : list2) {
//The if is in case the 2 lists aren't filled with the same objects
if (tempMap.containsKey(vv.name)) {
tempMap.get(vv.name).description = vv.description;
} else {
tempMap.put(vv.name, vv);
}
}
return new ArrayList<Vo>(tempMap.values());
}
If the lists contain both EXACT the same VO (equal by name), you can use this.
public List<Vo> merge(List<Vo> list1, List<Vo> list2) {
Collections.sort(list1, new Comparator<Vo>() {
public int compare(Vo o1, Vo o2) {
return o1.name.compareTo(o2.name);
}
});
Collections.sort(list2, new Comparator<Vo>() {
public int compare(Vo o1, Vo o2) {
return o1.name.compareTo(o2.name);
}
});
for(int i = 0; i < list1.size(); i++){
list1.get(i).description = list2.get(i).description;
}
return list1;
}
Put all elements of the first list in a map, then merge the contents of the second list into it:
final List<PersonDetails> listWithAddress =
new ArrayList<PersonDetails>();
final List<PersonDetails> listWithDescription =
new ArrayList<PersonDetails>();
// fill both lists with data
final Map<String, PersonDetails> map =
// map sorted by name, change to HashMap otherwise
// (or to LinkHashMap if you need to preserve the order)
new TreeMap<String, PersonDetails>();
for(final PersonDetails detailsWithAddress : listWithAddress){
map.put(detailsWithAddress.getName(), detailsWithAddress);
}
for(final PersonDetails detailsWithDescription : listWithDescription){
final PersonDetails retrieved =
map.get(detailsWithDescription.getName());
if(retrieved == null){
map.put(detailsWithDescription.getName(),
detailsWithDescription);
} else{
retrieved.setDescription(detailsWithDescription.getDescription());
}
}
I would put each list of the source VO lists in a map by name, create a set of both keys, iterate it and add a target VO to the result list.
In the example code, VO is the target VO, VOD is the source VO with description only, VOA is the source VO with address only:
List<VOD> descriptions = ...;
List<VOA> addresses = ...;
Map<String,String> description ByName = new HashMap<String,String>();
for (VOD description : descriptions) {
descriptionByName.put(description.name, description.description);
}
Map<String,String> addressByName = new HashMap<String,String>();
for (VOA address: addresses ) {
addressByName.put(address.name, address.address);
}
Set<String> allNames = new HashSet<String>();
allNames.addAll(descriptionByName.keySet());
allNames.addAll(addressByName.keySet());
List<VO> result = new ArrayList<VO>();
for (String name : allNames) {
VO one = new VO();
one.name = name;
one.address = addressByName.get(name)
one.description = descriptionByName.get(name)
result.add(one);
}
Go from list to HashMap, use the name string as key. So you can merge you data quite efficient.
List<VO> list = new ArrayList<VO>();
for (VO vo1 : vo1List) {
for (VO vo2 : vo2List) {
if (vo1.getName().equals(vo2.getName())) {
VO newVo = new VO();
newVO.setName(vo1.getName());
newVO.setDescription(vo1.getDescription());
newVO.setAddress(vo2.getAddress);
list.add(newVO);
break;
}
}
}
It's best that you sort both lists on name beforehand, it makes the double iteration faster.
Related
I have the following code:
HashMap<String, HashSet<Person>> index = new HashMap<String, HashSet<Person>>();
public static void indexDB(String base)
{
for(Person i: listB)
{
if(name.equals(base))
{
}
}
listB is an array with Person elements.
So, if a Person's name matches the String base, they are getting attached to a pair of key-value in the index HashMap. The HashSet for each key contains the Persons that their name matches the String base. How can this be done?
Also, I have a method like:
public void printPersons(String sth)
{
}
that I want it to print the persons contained in the HashSet of the key called each time.
Thank you
Use putIfAbsent to insert an empty hash set place holder.
Then add new person to existing set:
HashMap<String, HashSet<Person>> index = new HashMap<String, HashSet<Person>>();
public static void indexDB(String base)
{
for(Person i: listB)
{
if(name.equals(base))
{
index.putIfAbsent(base, new HashSet<>());
index.get(base).add(i)
}
}
Note: In order to correctly add person to set, you have to implement equals()/hashCode() for your Person class, since Set use equals() to determine uniqueness
Instead of creating HashSet object in every iteration, create it only when name matches like in the below code -
public static void indexDB(String base)
{
for(Person i: listB)
{
if(index.containsKey(base)){
HashSet<Person> existingHS = index.get(base);
existingHS.add(i);
index.put(base,existingHS);
}else{
HashSet<Person> hs = new HashSet<Person>();
hs.add(i);
index.put(base,hs);
}
}
Do this
HashMap<String, HashSet<Person>> index = new HashMap<String, HashSet<Person>>();
public static void indexDB(String base)
{
HashSet<Person> h = new HashSet<String>();
for(Person i: listB)
{
//I assume it is i.name here
if(i.name.equals(base))
{
h.add(i);
}
}
index.put(base,h);
}
And for printing, do this
public void printPersons(String sth)
{
Map mp = index.get(sth);
Iterator it = mp.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getKey() + " = " + pair.getValue());
}
}
I want to pass two lambda expressions (or something similar, I'm still getting familiar with all the terminology) into a method; the first one will get a list of items, and the second one will retrieve one Integer object from (each one of) those items.
So I want to have a method something like this:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Integer>> listGetter,
Supplier<Integer> idGetter)
{
List<Object> objectList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object o: objectList)
{
idList.add(idGetter.get());
}
return idList;
}
and call it something like this:
List<Integer> idList = setRecentList(searchParams,
(a) -> serviceOne.getItemList(a),
Item::getItemId);
So the first function is one called on an instance variable that the called method will have access to, and the second function is an instance method on any one of the objects returned as a list by the first function.
But the (eclipse) compiler doesn't like Item::getItemId, with or without parentheses at the end.
Do I just have a syntax thing wrong, or is there something else wrong with this idea?
Edit after many helpful comments -- thanks to you all!
I have one problem left. I've now got a method that I think does what I want, but I'm not sure how to pass the second expression to call it. Here is the method:
private List<Integer> getRecentList(List<Integer> salesCodeIdList,
Function<List<Integer>, List> listGetter,
Function<Object, Integer> idGetter
) {
List<Object> objectList = listGetter.apply(salesCodeIdList);
List<Integer> idList = new ArrayList<>();
for (Object o : objectList) {
idList.add(idGetter.apply((o)));
}
return idList;
}
AFAIK, I have to leave the second List raw in the getter spec, since different getters will return different types of objects in their lists.
But I still don't know how to invoke it -- I want to pass a method that gets an id from a particular instance of object, i.e., I want to pass a getter on the ID of one of the objects returned by the listGetter. That will be different types of objects on different calls. How would I invoke that?
To go back to examples, if I had a Supplier class with getSupplierId(), and a Vendor class with getVendorId(), and I cannot change those classes, can I pass in the correct method to invoke on the objects in the list depending on which getter retrieved the list?
A likely reason why you get the error is hidden inside the implementation of your method:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Object>> listGetter,
Supplier<Integer> idGetter)
{
List<Object> objectList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object o: objectList)
{
idList.add(idGetter.get()); // <<<<<==== Here
}
return idList;
}
Note how o from the for loop is not used in the call, indicating that idGetter would get you an ID out of thin air. That's of course is not true: you need to pass an item to idGetter, which means that the call should be
idList.add(idGetter.apply(o));
(from the comment) I can't cast within the method, I don't know what type of object to cast to there.
which in turn means that idGetter should be Function<Object,Integer>:
for (Object o: objectList)
{
idList.add(idGetter.apply(o));
}
Since you would like to reuse the same function for lists of different types, the call would have to use a lambda that performs the cast at the caller, i.e. at the point where you know the type:
List<Integer> idList = setRecentList(searchParams,
(a) -> serviceOne.getItemList(a),
(o) -> ((Item)o).getItemId());
Item::getItemId is not a Supplier<Integer>. A Supplier<Integer> is a lambda function that takes no arguments and returns an Integer. But Item::getItemId would require an Item to get the ID from.
Here is probably what you want. Note I've changed your method signature to match the idea that you're getting IDs from items.
class Item {
int id;
Item (int i) {
id = i;
}
int getItemId() {
return id;
}
}
private static List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Item>> listGetter,
Function<Item, Integer> idGetter)
{
List<Item> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Item item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
Edit: If you want this to handle multiple kinds of Items, and they all have IDs, you can do something like the following. First, define an interface to represent the fact that your different kinds of Items all have IDs:
interface ItemWithId {
abstract int getItemId();
}
class ItemA implements ItemWithId {
int id;
ItemA(int i) {
id = i;
}
public int getItemId() {
return id;
}
}
class ItemB implements ItemWithId {
int id;
ItemB(int i) {
id = i;
}
public int getItemId() {
return id;
}
}
Then, your method should use ItemWithId instead of Item:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<ItemWithId>> listGetter,
Function<ItemWithId, Integer> idGetter)
{
List<ItemWithId> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (ItemWithId item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
Finally, you can call this by casting an ItemA or ItemB to an ItemWithId:
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemWithId) item).getItemId());
Another edit: Ok, so you can't change ItemA or ItemB. You can still make it work:
class ItemA {
int id;
ItemA(int i) {
id = i;
}
public int getItemIdA() {
return id;
}
}
class ItemB {
int id;
ItemB(int i) {
id = i;
}
public int getItemIdB() {
return id;
}
}
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Object>> listGetter,
Function<Object, Integer> idGetter)
{
List<Object> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemA) item).getItemIdA());
// or
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemB) item).getItemIdB());
I am struggling with following:
I need to create a method which returns a collection of all values that specify some particular selection criterion specified by one or more arguments.
My MAP consists of PPS numbers(keys) and values( town, name, surname, place of work ) Both are strings .
However, I am not sure what I need to do to get the values after placin in the map.
/**
*This method returns a collection of all people who work for CO-OP
*/
public Set<String> selectKeys(String factory)
{
for (Set<String>eachTaxPayers : taxPayersList.values())
{
if(taxPayersList.values().contains(factory))
{
Set<String>eachClients = taxPayersList.keySet();
System.out.println(taxPayersList.keySet());
}
}
return null ;
}
Could someone help me please?
This is a code how Map is populated.
public class Tax
{
Map<String, Set<String>>taxPayersList;
public Tax()
{
taxPayersList = new HashMap<>();
Set<String>taxPayersDetails = new HashSet<>();
taxPayersDetails.add(" Eddie Donegan");
taxPayersDetails.add("Prodieco");
taxPayersDetails.add("Limerick");
taxPayersList.put("4481908A", taxPayersDetails);
taxPayersDetails = new HashSet<>();
taxPayersDetails.add(" Paddy Power");
taxPayersDetails.add("Covenant");
taxPayersDetails.add("Limerick");
taxPayersList.put("6088989B", taxPayersDetails);
taxPayersDetails = new HashSet<>();
taxPayersDetails.add(" Mikey Obama");
taxPayersDetails.add("Prodieco");
taxPayersDetails.add("Limerick");
taxPayersList.put("6788910B", taxPayersDetails);
}
}
I want only to return the key's( PPS numbers) for people who works for the same company
public Set<String> selectKeys(String factory) {
// our return Set
Set<String> factoryWorkers = new HashSet<>();
// Let's iterate over the map entries
for (Map.Entry entry : taxPayersList.entrySet()) {
// Let's grab the value of the current map entruy
Set<String> eachTaxPayers = entry.getValue()
// Risky move
if(eachTaxPayers.contains(factory)) {
// add the key (PPS) to the return set
factoryWorkers.add(entry.getKey());
}
}
return factoryWorkers;
}
FYI, the line marked as "Risky Move" is not the best approach.
Though unlikely, it's possible a city has the same name as factory.
You'd be better using an Iterator on the Set and comparing against the 2nd value.
Even better, instead of having a Map>
you could have a Map
where Employee has fields such as name, city and factoryName.
I have an array list which when populated has a key and a value I want to know if there is a way of splitting it on repeating keys for example my current data is like this:
[RoleID_123.0, UserHandel_tom, Password_12345.0, prevPassword_null, userCaption_thomas, Email_tom#tom.tom, RoleID_124.0, UserHandel_dave, Password_ghadf, prevPassword_sdfsd, userCaption_david, Email_dave#dave.dave, RoleID_125.0, UserHandel_trevor, Password_tre, prevPassword_null, userCaption_trev, Email_trev#trev.trev]
I want it to come out more like this:
[RoleID_123.0, UserHandel_tom, Password_12345.0, prevPassword_null, userCaption_thomas, Email_tom#tom.tom]
[RoleID_124.0, UserHandel_dave, Password_ghadf, prevPassword_sdfsd, userCaption_david, Email_dave#dave.dave]
[RoleID_125.0, UserHandel_trevor, Password_tre, prevPassword_null, userCaption_trev, Email_trev#trev.trev]
Is there a way to split it on say role id or am I going about this the wrong way?
You can try by using HashMap
private static class MyItemHashMap extends HashMap {
public Item add(Item item) {
get(item).add(item);
return item;
}
public List get(Item key) {
List list = (List) get(createItemKey((Item) key));
return list == null ? createItemEntry((Item) key) : list;
}
private List createItemEntry(Item item) {
List list = new ArrayList();
put(createItemKey(item), list);
return list;
}
private Object createItemKey(Item item) {
return item.getSplitterProperty();
}
}
public static void main(String[] args) {
MyItemHashMap itemMapped = new MyItemHashMap();
List items = Arrays.asList(new Object[]{new Item("A"), new Item("B"),
new Item("C")});
for (Iterator iter = items.iterator(); iter.hasNext();) {
Item item = (Item) iter.next();
itemMapped.add(item);
}
}
If it is an ArrayList, there is no built-in function to split data like this; you will have to do it manually. If you know the number of consecutive fields that make a single structure, this shouldn't be too hard; something like this:
// 6 because there are 6 fields
for (int i = 0; i < arrayList.size(); i = i + 6) {
List thisList = arrayList.subList(i, i + 5);
// ... Now do whatever you want with thisList - it contains one structure.
}
If the number of fields can change then you'll have to do something a little more dynamic and loop through looking for a RoleID field, for example.
I'd use a HashMap to seperate the data instead of one long ArrayList ( you shouldn't have stored the data like this in the first instance )
HashMap<String,ArrayList<String>> hm = new HashMap<String,ArrayList<String>>;
// For each list:
ArrayList<String> arr = new ArrayList<String>;
arr.add("each element");
hm.put("RoleID_123.0", arr);
This way you will end up with a three dimensional structure with a key ( "RoleID..." ) pointing to its child elements.
Try this
String[] str=new String[]{"RoleID_123.0", "UserHandel_tom", "Password_12345.0", "prevPassword_null", "userCaption_thomas", "Email_tom#tom.tom", "RoleID_124.0", "UserHandel_dave", "Password_ghadf", "prevPassword_sdfsd", "userCaption_david", "Email_dave#dave.dave", "RoleID_125.0", "UserHandel_trevor", "Password_tre", "prevPassword_null", "userCaption_trev", "Email_trev#trev.trev"};
List<String> list=new ArrayList<>(Arrays.asList(str));
List<String> subList=list.subList(0,5);
You can try something similar to this
If you feel like taking a Linq-ee Libraried approach, this is about as good as it gets, and it requires use of a couple delegate objects:
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Collections2.transform;
//...
final List<String> yourList = //...
final int RECORD_LENGTH = 6;
Collection<String> roleIdValues = filter(yourList, new Predicate<String>() {
public boolean apply(#Nullable String input) {
return input != null && input.startsWith("RoleID");
}
});
Collection<Collection<String>> splitRecords = transform(roleIdValues, new Function<String, Collection<String>>() {
#Nullable public Collection<String> apply(#Nullable String input) {
return yourList.subList(yourList.indexOf(input), RECORD_LENGTH);
}
});
If Oracle had delivered Java 8 on time you would be able to do this in a way more slick manor. Ironically the reason you cant was provided by the same people providing the guava library
I have a list of objects in this format :
class myObj {
private String identifier;
public myObj(String identifier){
this.identifier = identifier;
}
}
List<myObj> allobjects = new ArrayList<myObj>();
allobjects.add(new myObj("123"));
allobjects.add(new myObj("123"));
allobjects.add(new myObj("123"));
allobjects.add(new myObj("123"));
allobjects.add(new myObj("1234"));
allobjects.add(new myObj("12345"));
allobjects.add(new myObj("12"));
allobjects.add(new myObj("12"));
What is an elegant method of extracting the duplicate objects into seperate Lists ?
So in above example a new List is returned containing two lists. The first lists
contains :
new myObj("123");
new myObj("123");
new myObj("123");
new myObj("123");
The second list contains :
new myObj("12");
new myObj("12");
A possible solution is to create a new object :
List<List<myObj>> newList = new ArrayList<List<myObj>>
And then for each element in the list 'allobjects' iterate over each element and for each element that is contained more than once add it to the list. Then at the
end of the iteration for the current element add the newly created list to 'newList'
Is this acceptable or is there another solution ?
Add equals and hashCode methods to the myObj class, so that you can use them as Map keys:
class myObj {
private String identifier;
public myObj(String identifier){
this.identifier = identifier;
}
public int hashCode(){
return identifier.hashCode();
}
public boolean equals(Object o){
return identifier.equals(((myObj)o).identifier);
}
}
Then declare a Map:
Map<myObj, List<myObj>> map = new HashMap<myObj, List<MyObj>>()
and iterate through the original list. Use the myObjs as a map key, retrieving each time a list that corresponds to that myObj. If you encounter a certain myObj for the first time, don't forget to create the list:
for(myObj obj : allobjects){
List<myObj> list = map.get(obj);
if(list == null){
list = new ArrayList<myObj>();
map.put(obj, list);
}
list.add(obj);
}
Implement equals as required and then you can just use contains and iterate through checking other collection.
Here's a way to do it with jdk8s' lambdas.
TransformService transformService = (inputs1, inputs2) -> {
Collection<String> results = new ArrayList<>();
for (String str : inputs1) {
if (inputs2.contains(str)) {
results.add(str);
}
}
return results;
};
Collection<String> inputs1 = new ArrayList<String>(2) {{
add("lemon");
add("cheese");
add("orange");
}};
Collection<String> inputs2 = new
ArrayList<String>(2) {{
add("apple");
add("random");
add("cheese");
}};
Collection<String> results = transformService.transform(inputs1, inputs2);
for (String result : results) {
System.out.println(result);
}
}
public interface TransformService {
Collection<String> transform(Collection<String> inputs1, Collection<String> inputs2);
}