I want to search value from object and convert result to list
I tried to get data object name "data" and i want to search every item that has "querytext".How to convert object data to List
public class SimpleMovieSearchService implements MovieSearchService {
#Autowired
private MovieDataService movieDataService;
#Override
public List<Movie> search(String queryText) {
MoviesResponse data =movieDataService.fetchAll();
List<Movie> result = data.stream() // problem in this line
.filter(item -> item.getTitle().equals("queryText"))
.collect(Collectors.toList());
return result;
}
}
MovieData.java
public class MovieData {
private String title;
private int year;
private List<String> cast;
private List<String> genres;
getter and setter
}
Movie.java
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ElementCollection(fetch = FetchType.EAGER)
private List<String> actors = new ArrayList<>();
getter and setter
}
edit
MovieResponse.java
public class MoviesResponse extends ArrayList<MovieData> {
}
MovieDataService.java
public interface MovieDataService {
MoviesResponse fetchAll();
}
Type of data i got is List of MovieData but i want result in List of Movie how to fix this or another way to do.and without change anything in Movie.java or MovieData.java
Update
now i can do with this way
MoviesResponse a = movieDataService.fetchAll();
List<Movie> result = a.stream()
.map(movie -> new Movie(movie.getTitle()))
.filter(movie -> movie.getName().equals(queryText)) //this line
.collect(Collectors.toList());
if data are "banana" ,"nature" , "red" and then query text is "na" i want result are "banana" and "nature".in sql in situation it can use "like".How to use "like" with this filter ?
Try to use stream map
data.stream() // problem in this line
.filter(item -> item.getTitle().equals("queryText"))
.map(movie -> {
//logic to transform MovieResponse in Movie here
})
.collect(Collectors.toList());
See more in: https://dzone.com/articles/how-to-use-map-filter-collect-of-stream-in-java-8
Instead of fetching all movies and filer them by stream, write a method that queries the ones you want.
Something like
SELECT * FROM movies WHERE title = 'queryText'
Depending on your code/framework, you have to implement this or just have to choose another method in you MovieDataService.
Related
I use lambda to convert objects one type to objects another
Order
#JacksonXmlRootElement(localName = "order")
public class Order {
private String customer;
#JacksonXmlProperty(localName = "orderItem")
#JacksonXmlElementWrapper(useWrapping = false)
private List<OrderItem> orderItems = new ArrayList<>();
public Order() {
}
OrderDto
public class OrderDto {
private String customer;
private String orderXml;
public OrderDto() {
}
service
#Service
public class OrderReadServiceImpl implements OrderReadService {
private OrderEntityRepository repository;
private OrderDtoMapper mapper;
private CycleAvoidingMappingContext context;
#Autowired
public OrderReadServiceImpl(OrderEntityRepository repository,
OrderDtoMapper mapper,
CycleAvoidingMappingContext context) {
this.repository = repository;
this.mapper = mapper;
this.context = context;
}
#Override
public Iterable<Order> getListOrder() {
Iterable<OrderEntity> orderEntities = this.repository.findAll();
Iterable<Order> orders = convertXmlToListObj(orderEntities);
return orders;
}
private Iterable<Order> convertXmlToListObj(Iterable<OrderEntity> entities) {
Iterable<OrderDto> dtoList = toListDto(entities);
Iterable<Order> orders = convertListToList(dtoList);
return orders;
}
/**
* There is convert a collection of objects one type to another type
* #param dtoList
* #return
*/
private static Iterable<Order> convertListToList(Iterable<OrderDto> dtoList) {
List<OrderDto> list = new ArrayList<>();
dtoList.forEach(list::add);
List<Order> collect = list.stream()
.map(orderDto -> {
Order order = convertXmlToObj(orderDto);
return order;
}).collect(Collectors.toList());
return collect;
}
/**
* there is got a string that xml. This xml is convert to java object
* #param orderDto
* #return
*/
private static Order convertXmlToObj(OrderDto orderDto) {
String orderXml = orderDto.getOrderXml();
StringReader reader = new StringReader(orderXml);
Order order = JAXB.unmarshal(reader, Order.class);
return order;
}
/**
* transform objects of entity type to objects of dto types
* #param entities
* #return
*/
private Iterable<OrderDto> toListDto(Iterable<OrderEntity> entities) {
return this.mapper.toListDto(entities);
}
}
The resulting list of entities is converted to a dto collection. The collection of the converted dto list is iterated over and retrieved from there xml from the field of each collection element and then the structure of this xml it will be umarshall (that is, the list of xml elements will be converted to the collection of java objects)
List<OrderDto> list = new ArrayList<>();
dtoList.forEach(list::add);
List<Order> collect = list.stream()
.map(orderDto -> {
Order order = convertXmlToObj(orderDto);
return order;
}).collect(Collectors.toList());
return collect;
I would desire to do simplest. I want the code to will be yet lesser.
Сan you remove the code somewhere, how to reduce it.
I mean . Where do I create the 'method references' yet.
Who has any ideas how to do this ?
You can convert the Iterable to Stream directly, without creating a List:
StreamSupport.stream(dtoList.spliterator(), false)
Your code can become
private static Iterable<Order> convertListToList(Iterable<OrderDto> dtoList)
{
return StreamSupport.stream(dtoList.spliterator(), false)
.map(orderDto -> convertXmlToObj(orderDto))
.collect(Collectors.toList());
}
Or with method reference:
private static Iterable<Order> convertListToList(Iterable<OrderDto> dtoList)
{
return StreamSupport.stream(dtoList.spliterator(), false)
.map(OrderReadServiceImpl::convertXmlToObj)
.collect(Collectors.toList());
}
BTW, since your method is named convertListToList(), perhaps it should accept and return Lists instead of Iterables.
If you want to solve the problem of mapping objects in general and are not looking exactly for an optimization of your lambda/stream solution, you could give MapStruct a look. Simplified description: It generates mappers with the help of annotations at compile time.
user class with fields id, name and types
public class User {
private String id;
private String name;
private List<Types> types;
}
Types class with fields id, name , linkedTo
{
private String id;
private String name;
private String linkedTo;
}
Implemented a method to get types from user and return it as list
private List<Types> getType(List<User> Users) {
return Users
.stream()
.map(User::gettypes)
.flatMap(List::stream)
.collect(Collectors.toList());
}}
Now, have to map to response body. For that I have response class:
public class UserResponse {
private String id;
private String type;
private String name;
private String linkedTo;
}
Have to map the fields from Types to user response to return it as list
private List<UserResponse> getUserResponse(UserRequest request) {
List<User> Users = userServiceClient.getById();
List<UserResponse> userResponses = new ArrayList<>();
List<Types> TypesList = getType(Users);
if (!typesList.isEmpty()) {
return typesList.stream()
.map(type -> userResponses.add(new UserResponse( type.getGuid(),type.getName(),type.getName(),type.getLinkedTo())))
.collect(Collectors.toList());
}
return collections.emptylist();
}
Here is the problem, I'm not able to return it as list instead getting a boolean..
Is there any efficient way to do this? The code reviewer doesn't want me to use for each and return the list
A better appraoch would be
private List<UserResponse> getUserResponse(UserRequest request) {
List<User> Users = userServiceClient.getById();
List<Types> TypesList = getType(Users);
return typesList.stream()
.map(type -> new UserResponse(type.getId(), type.getName(), type.getName(), type.getLinkedTo()))
.collect(Collectors.toList());
}
Reason:
If typesList is empty, this would return empty List, as Collections.emptyList in your code.
It maps to the UserResponse objects and collects them to return the List<UserResponse> while using the Stream for it without the explicit instantiation and .add call.
I have the following 2 objects Team and Group. I have standard getters setters and toString methods in each of these classes and not allowed to modify them.
public class Team {
private List<Team> teams;
private List<TeamMember> members;
private String teamId;
}
public class Group {
private List<GroupMember> groupMember;
private List<Group> groups;
private String groupId;
}
Team can have a List<Team> type of a list as an attribute where List<Group> can have a List<Group> as an attribute.
example list of Teams look like follows:
I want to create the list of Groups which reflects the same structure of TeamList.
This is what I have got so far.
#Service
public class GroupService {
#Autowired
TeamService teamService;
public List<Group> createGroupList(){
List<Group> groups = Collections.emptyList();
List<Team> teams = teamService.createTeamList();
if (teams != null && !teams.isEmpty()) {
groups = teams.stream().map(t -> {
Group group = new Group();
group.setGroupId(t.getTeamId());
//this is to be modified
group.setGroups(getSubgroups(teams, group.getGroupId()));
return group;
}).collect(Collectors.toList());
}
return groups;
}
private List<Group> getSubgroups(List<Team> teams, String parentGroupName) {
Optional<Team> parentTeam = teams.stream()
.filter(t -> t.getTeamId().equalsIgnoreCase(parentGroupName)).findFirst();
if(parentTeam.isPresent()){
List<Team> subTeams = new ArrayList<>();
List<Group> lstOfGroups = new ArrayList<>();
System.out.println("parentname " + parentTeam.get().getTeamId());
if(parentTeam.get().getTeams() != null){
parentTeam.get().getTeams().stream().forEach(r -> {
subTeams.add(r);
});
}
subTeams.stream().forEach(st -> {
Group gr = new Group();
gr.setGroupId(st.getTeamId());
lstOfGroups.add(gr);
});
return lstOfGroups;
}
return null;
}
}
My idea is to modify the getSubgroups method to set the subgroups correctly for each given path.(eg: getSubgroubs can return team2 with all it's subgroups set until team7) I know I have to use recursion but I am struggling to find the solution. How can I achieve this?
EDIT
I have updated my code and now I can access the first level of children and not the other levels yet
You can just create a single method to copy one into the other and call it recursively:
public Group toGroup(Team team) {
Group result = new Group(team.teamId());
// this is missing in your sample code
result.setGroupMembers(transform(team.getTeamMembers());
List<Group> subGroups = team.getTeams().stream()
.map(this::toGroup) // recursive call to this method
.collect(toList());
result.setSubgroups(subGroups);
return result;
}
so you can do
List<Group> groups = teamService.getTeams()
// you should return an empty list if no elements are present
.stream()
.map(this::toGroup) // initial call
.collect(toList());
You might also want to look into mapstruct which can automatically generate simple mappers.
To give you an idea how this would look in mapstruct:
#Mapper(componentModel="spring")
interface TeamGroupMapper {
#Mappings({
#Mapping(source="teamId", target="groupId"),
#Mapping(source="teams", target="groups"),
#Mapping(source="teamMembers", target="groupMembers")
})
Group toGroup(Team team);
List<Group> toGroups(List<Team> teams);
GroupMember toGroupMember(TeamMember teamMember);
}
The actual code will be generated. If the classes have properties with the same name (eg if the ids were called id for both Team and Group?), a #Mapping annotation is not needed for it.
Then, you can #Autowire this as a component and use it.
#Component
class YourGroupService implements GroupService {
#Autowired TeamGroupMapper mapper;
#Autowired TeamService teamService;
public List<Group> getGroups() {
return mapper.toGroups(teamService.getTeams());
}
}
I'm sure this code won't actually work, but it should give you an idea of what mapstruct does. I really like it to avoid boilerplate mapping code.
I am using spring-boot-1.5.6 and modelmapper-1.1.0. I would like to map the entity object to OrderDto
but don't know how to do it via modelMapper. Please find the below code
Order.Java
public class Order {
private String orderUid;
private Invoice invoice;
private List<Item> items;
//Getter & setter
}
Item.java
public class Item {
private String itemId;
private String itemName;
private String itemUid;
private String isbn;
//other details of item
//Getter & setter
}
OrderDTO.java
public class OrderDTO {
private String orderUid;
private Invoice invoice;
private String itemId;
private String itemName;
private String itemUid;
private String isbn;
//other details of item
//Getter & setter
}
I would like to return OrderDTO with the item based on the itemID we are getting from the client(FrontEnd)
public Page<OrderDTO> convertOrderEntityToDTO (Page<Order> orderList,String itemId) {
ModelMapper modelMapper = new ModelMapper();
Type listType = new TypeToken<Page<OrderDTO>>() {}.getType();
modelMapper.addConverter((MappingContext<Order, OrderDTO> context) -> {
Item item = context.getSource().getItems().stream()
.filter(item -> equalsIgnoreCase(item.getItemId,itemId))
.findAny().orElse(null);
if (item != null) {
OrderDTO orderDTO = context.getDestination();
orderDTO.setItemId(item.getItemId());
orderDTO.setItemName(item.getItemName());
orderDTO.setItemUid(item.getItemUid());
orderDTO.setIsbn(item.getIsbn());
return orderDTO;
}
return null;
});
Page<OrderDTO> addonServices = modelMapper.map(orderList, listType);
}
In the above method, converter was never called(may be because of incorrect TypePair of modelMapper) and the item related attributes
in OrderDTO is always null. I would like to get the Item value based on ItemId.
Any help or hint would be appreciable. Any suggestion with or without modelMapper would be really appreciable.
As far as I understand, you use Page class of org.springframework.data or something like that. Anyway, this generic Page class contains generic List with your data. I would say, that this construction is just "too generic" for modelMapper. You'd better get list of your data, convert it and create a new Page.
Furthermore
You should create a typeMap to register a new converter
context.getDestination() returns not null if you provide new destination object by modelMapper.map(..). In your case you'll get null.
This is my vision of your convertOrderEntityToDTO method:
public Page<OrderDTO> convertOrderEntityToDTO(Page<Order> orderList, String itemId) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.createTypeMap(Order.class, OrderDTO.class)
.setConverter(context -> context.getSource().getItems().stream()
.filter(o -> equalsIgnoreCase(o.getItemId(), itemId))
.findAny().map(o -> {
OrderDTO orderDTO = new OrderDTO();
orderDTO.setItemId(o.getItemId());
orderDTO.setItemName(o.getItemName());
orderDTO.setItemUid(o.getItemUid());
orderDTO.setIsbn(o.getIsbn());
return orderDTO;
}).orElse(null));
List<OrderDTO> orderDtoList = orderList.getContent().stream()
.map(o -> modelMapper.map(o, OrderDTO.class))
.collect(Collectors.toList());
return new PageImpl<>(orderDtoList, orderList.getPageable(), orderList.getTotalElements());
}
If you are using Spring Data JpaRepositories to retrieve data from the database you can make a repository method that creates a DTO for you. Such a method would look something like this:
#Query("SELECT new full.path.OrderDTO(s.itemId, s.itemName, s.itemUid) FROM Item s WHERE s.itemId = :id")
public OrderDTO getOrderDTOByItemId(#Param("id") long id);
The method should go in the jparepository class you use to retrieve Item instances from the database with. To make this work your OrderDTO needs to have a constructor with this parameter list. ( long itemId, String itemName, String itemUid). The parameters need to be in the same order as (s.itemId, s.itemName, s.itemUid). Also always make sure you create a default constructor as well when you create a constructor with parameters.
I have following code :
private String categoryId;
List<Category> categories = new List<>();
for(String category:categories){
if(category.getName().equals(categoryName)){
categoryId = category.getId();
break;
}
}
I want to use stream api here to get categoryId. My category class as follows.
class Category{
private String name;
private String id;
// gettters and setters.
}
category Id is assigned randomly when a new category is created.
Thanks in advance.
try to use this :
categoryId = categories.stream()
.filter(category -> category.getName().equals(categoryName))//filter by name
.map(Category::getId) //get only the ids
.findFirst() //return just the first result
.orElse(null); //if no result then return null
If you want to use Streams, you can write:
categories.stream().filter(c -> {
if (c.getName().equals(categoryName)) categoryId = c.getId()});