Find all objects in list that contain a certain field - java

I'm trying to find all the objects in a list of objects that contain a particular field name. For example
"list": [
{
"namesArray": [],
"name": "Bob",
"id": "12345",
},
{
"namesArray": [
"Jenny"
],
"name": "Ned",
},
{
"namesArray": [],
"name": "Jane",
"id": "gkggglg",
}
]
The class looks like this:
class ListItem {
String id;
String name;
List<String> namesArray;
}
So basically I need to find all the objects that contain the field "id". Something like:
list.stream().filter(li -> li.equals("id")).collect(Collectors.toList());
I've tried following this page and it isn't quite what I want. I don't care about the values of the id's, just whether or not the object has the field at all.

From the comments, we get your actual requirement:
So all objects with a non-null id field.
It's easy to adapt the code you've already got using streams and a filter - you just need to change the predicate that's being passed to the filter method. That predicate needs to return true for any value you want to be in the result, and false for any value you want to be discarded. So all you need is:
var result = list
.stream()
.filter(item -> item.id != null)
.collect(Collectors.toList());

Related

How could I get specific parameters just by passing them in the request? SpringBoot API

I'm struggling to find a solution for this...
I would like to know how to get a specified parameter passing it through the request without having to code each specific case in Spring Boot API.
For example, in my case I have this JSON object:
{
"id": 3,
"name": "test",
"dominio": "dom",
"altas": "6",
"bajas": "2",
"default_group": [
{
"idRef": 1,
"name": "Users",
"path": "OU=es"
}
],
"office": [
{
"idRef": 1,
"title": "Intern",
"name": "CN=Office license",
"path": "OU=licenseOffice"
},
{
"idRef": 2,
"title": "Specialist",
"name": "CN=Office License F3",
"path": "OU=LicenseGroupF"
}
]
Apart from this, I have all the entities defined in my code, but I would like to access one of their parameters just using their names, like name, dominio, altas, bajas, default_group or office.
The idea is to do this without having to code each method for each parameter.
I wouldn't have to do this for the nested objects (office and default_group) just getting the info from them passing the name of the parameter.
So I would like to do something like:
GET --> localhost:8080/api/3/name
And this would return the name of object with id 3
Or doing this:
GET --> localhost:8080/api/3/default_group
And this would return the Array containing all the default_groups inside.
Apart from this, I would like to know if is it possible to do a PUT request for the methods doing the same thing.
I don't know if this can be done, but in case that it can, would it be possible for you to give some guidance or something...
Thank you very much
Edit. Thanks to #daniu I made it work flawlessly, I paste my solution here based on his comment so if anybody find it helpful. My object is called "Compania".
#GetMapping("/{companiaId}/{field_name}")
public Object queryField(
#PathVariable("companiaId") Long companiaId,
#PathVariable("field_name") String fieldName) {
Map<String, Function<Compania, Object>> fieldRetrievers = Map.of(
"name", Compania::getName,
"dominio", Compania::getDominio,
"altas", Compania::getAltas,
"bajas", Compania::getBajas,
"default_group", Compania::getDefault_group,
"office", Compania::getOffice
);
Compania c = companiaService.getCompaniaNotOpt(companiaId);
Function<Compania, Object> retriever = fieldRetrievers.get(fieldName);
return retriever.apply(c);
}
getCompaniaNotOpt is a method that takes a Compania without being Optional, so it works.
Thanks daniu.
I wouldn't consider this the cleanest of designs, but what would work is creating a Map that contains all the field accessors by name:
Map<String, Function<YourClass>> fieldRetrievers = Map.of(
"name", YourClass::getName,
"default_group", YourClass::getDefaultGroup,
"office", YourClass::getOffice
);
Then you can use that in your controller (service actually, but to keep it short here):
#GetMapping("/path/{field_name}")
Object queryField(#PathVariable("field_name") String fieldName) {
YourClass c = getObject();
Function<YourClass, Object> retriever = fieldRetrievers.get(fieldName);
return retriever.apply(c);
}

Return 1st object from the array matching any value from input array using Java 8

I have a list of values 1,2,3,4. It depends on my how I want to struct these values, be it Array, ArrayList, etc.
Then, I have the response coming from rest call which may or may not contain these values. My object is to return 1st object from the response's field4 which contains these values. The structure of response will be like below. In this case, I would like to return 2nd object from the array since 4 is the 1st match with given input.
{
"field1": "",
"field2": "null",
"responseArray": [
{
"field3": "abc",
"field4": "8",
"field5": "def"
},
{
"field3": "abc",
"field4": "4",
"field5": "def"
},
{
"field3": "abc",
"field4": "1",
"field5": "def"
}
]
}
I understand I can do brute-force method where I can traverse through each object of the response, then match field4 with given input values and once match is found, exit the loop so as to skip traversing rest of the loop. But, is there another effective way that can be used here specially with features from java8?
If you use a Set for your values, then something like this would work:
(assuming your responseArray is an array of objects that has a getField4 method)
Arrays.stream(responseArray).findFirst(e -> values.contains(e.getField4()));

Get String value if present or null if not present using Java8 Stream

I have a json similar like as shown below. The requirement is to get the key value based on the id. ie. lets say If Id is A1 key value should return 2gAwIBAgIQKGZsKfAUzaJHVantyrwVdzANBgkqhkiG9w0BAQs. The key array will always conatins only one element.
{
"keys": [
{
"id": "A1",
"key": [
"2gAwIBAgIQKGZsKfAUzaJHVantyrwVdzANBgkqhkiG9w0BAQs"
]
},
{
"id": "A2",
"key": [
"swKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sfsf2dew"
]
},
{
"id": "A3",
"key": [
"EyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3Mubdwe2"
]
}
]
}
To implement the above I have wrote the below code using Java8 Stream but the problem is that it returns Optional<KeyDetails> which again I needs to parse and get the key value
String keyDetails = "{\"keys\":[{\"id\":\"A1\",\"key\":[\"2gAwIBAgIQKGZsKfAUzaJHVantyrwVdzANBgkqhkiG9w0BAQs\"]},{\"id\":\"A2\",\"key\":[\"swKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sfsf2dew\"]},{\"id\":\"A3\",\"key\":[\"EyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3Mubdwe2\"]}]}";
AccessKeys accessKeys = new ObjectMapper().readValue(keyDetails, AccessKeys.class);
Optional<KeyDetails> filteredKey = accessKeys.getKeys().stream().filter(key-> key.getId().equals("A3")).findAny();
if(filteredKey.isPresent()) {
String keyValue = filteredKey.get().getKey().get(0);
System.out.println("keyValue==>"+keyValue);
}
What I want is to get Optional<String> instead of Optional<KeyDetails>. i.e if the id is present it should return just the key value which is present within the key array.
Some condition that the json satisfies were:
Sometimes there can be a situation for duplicate key id, in that case it should pick only one (first one)
Key array will always contains one string value
Can anyone please help me on this
Sometimes there can be a situation for duplicate key id, in that case
it should pick only one (first one)
You're looking for the map method (to transform) and findFirst instead of findAny to always guarantee the first match is returned.
accessKeys.getKeys()
.stream()
.filter(key-> key.getId().equals("A3"))
.findFirst()
.map(e -> e.getKey().get(0)).orElse(null);
findAny, as the name suggests, should be used in cases where you don't care which matched item is returned. Yes, both findFirst and findAny will act the same in a sequential stream but I'd rather make my intention clear in the first place.

Sorting and Filtering List of Objects

I am having an external service from where I get all employees details of an organization like as shown below. I am using java8 and spring cloud feign client for consuming the service
[
{
"employee": {
"empId": "empId123",
"name": "Emp1",
"houseNumber": "5",
"firstName": "firstName1",
"lastName": "lastName1",
"city": "city1",
"band": "A"
},
"type": "ABC"
},
{
"employee": {
"empId": "empId456",
"name": "Emp2",
"houseNumber": "7",
"firstName": "firstName2",
"lastName": "lastName2",
"city": "city2",
"band": "B"
},
"type": "ABC"
}
:
:
]
The employees details service has around 10000+ employee details.
I have a requirement to create another two service
Sort based on city and houseNumber and return all employees
Service for filter employees based on certain attributes like city, band, empId etc.
At present for Sorting Service I am using like as shown below
final List<Employees> employeesList = employeeService.getAllEmployees().stream()
.sorted((emp1, emp2) -> p1.getAddress().getCity().compareTo(emp2.getAddress().getCity()))
.sorted((emp1, emp2) -> p1.getAddress().getHouseNumber().compareTo(emp2.getAddress().getHouseNumber()))
.collect(Collectors.toList());
For filtering I am using the below code
String cityName = "some city name"...
final List<Employees> employeesfilteredList = employeeService.getAllEmployees()
.stream()
.filter(employee -> employee.getAddress().getCity().equalsIgnoreCase(cityName == null ? "" : cityName))
.collect(Collectors.toList());
but my customer who is a technical guy says this has performance issues and asked to bring something which takes less time complexity (best to be O(1)) for bringing the result
Can anyone tell me what is the problem with the current approach which I'm using and is there any way in which I can improvise it in any other way or approach
One thing I could think of that you can certainly improvise is the call to sorted twice which can be made only once:
// replacing with 'employees' for 'employeeService.getAllEmployees()'
Comparator<Employees> compareBasedOnCity =
Comparator.comparing(emp -> emp.getAddress().getCity());
Comparator<Employees> compareBasedOnHouse =
Comparator.comparing(emp -> emp.getAddress().getHouseNumber());
employees.sort(compareBasedOnCity.thenComparing(compareBasedOnHouse));
and another during filter is to avoid treating null and "" string as same:
List<Employees> finalList = employees.stream()
.filter(employee -> employee.getAddress().getCity().equalsIgnoreCase(cityName))
// don't consider empty city name same as null (think of " " otherwise)
.collect(Collectors.toList());
But, as already pointed out by both Holger and JB Nizet, none of this brings down the complexity from say O(nlogn) to O(1) as you're expecting.
Comparing it further with operations like Access, Insertion and Deletion is not equivalent either. Since the operations performed their are different as well.

RestAssured JsonPath: How to filter json objects from json

I'm trying to take an array of objects from json file and i have an issue.
path.get("wgcTournaments.items")
What path i should use to get all items(item0, item1, item2 ...) in items?
Can you please give me an advice how to do it.
Json example
{
"wgcTournaments": {
"items": {
"jcr:primaryType": "nt:unstructured",
"item0": {
"jcr:primaryType": "nt:unstructured",
"test": "test",
"test1": "test1"
},
"item1": {
"jcr:primaryType": "nt:unstructured",
"test": "test",
"test1": "test1"
},
"item2": {
"jcr:primaryType": "nt:unstructured",
"test": "test",
"test1": "test1"
},
"item3": {
"jcr:primaryType": "nt:unstructured",
"test": "test",
"test1": "test1"
}
}
}
}
The best way to filter item from items object but i don't understand how to do it with json path.
Finally i found a solution for my question.
If you want to get item from items you need to use this one json Path
path.getObject("wgcTournaments.items*.
find{it.key.startsWith('item')}.value",ItemClass[].class);
Note:
it was RestAssured and he uses Gpath more details you can find here
http://docs.groovy-lang.org/latest/html/documentation/#_gpath
You are trying to deserialize an object into an array of objects. Either your code or your JSON (most likely) is wrong.
If you want to deserialize items as an array, your JSON should be the following:
{
"wgcTournaments": {
"items": [
{
"jcr:primaryType": "nt:unstructured",
"item0": {},
"item1": {},
"item2": {},
"item3": {}
}
]
}
}
Otherwise, if your JSON is correct, you should deserialize your JSON using the following line:
path.getObject("wgcTournaments.items", MyClass.class)
EDIT: After your edit, this seems to be what you want:
If your JSON in correct and you indeed want an array, I assume that each itemX is a the key and {} the corresponding value. In this case, you have to know that you cannot have an associative array in JSON, you should use a custom solution to deserialize it, because your associative array will be converted into an object.

Categories