I have the following code :
List<Object> result = new ArrayList<Object>();
//Object is actually a Map<String,Object>
return Maps.uniqueIndex(result, new Function<Map<String, Object>, Long>() {
#Override
public Long apply(Map<String, Object> input) {
return (Long) input.remove("id");
}
});
I get compilation error.
The method uniqueIndex(Iterable<V>, Function<? super V,K>) in the type Maps is not applicable for the arguments (List, new Function<Map<String,Object>,Long>(){}).
How do I rewrite this piece of code such that I don't get into this issue?
The first generic parameter of Function must match the type of elements held by List.
So, if you have a List<T>, a Function will be used for doing something with elements from that List, hence it needs to be a Function<T, WHATEVER>.
So, in your case:
List<Object> result = new ArrayList<>();
Maps.uniqueIndex(result, new Function<Object, WHATEVER>() {
#Nullable
#Override
public WHATEVER apply(#Nullable Object s) {
return null; // do whatever you want here
}
});
If you want to store Map<String,Object> in a List why not use List<Map<String,Object>>?
List<Map<String,Object>> result = new ArrayList<>();
Maps.uniqueIndex(result, new Function<Map<String,Object>, WHATEVER>() {
#Nullable
#Override
public WHATEVER apply(#Nullable Map<String,Object> s) {
return null; // do whatever you want here
}
});
Related
we are a small development team.
We develop in ASP.NET and we are starting to use generic controllers and services.
The goal is to have solid methods for things that are repetitive.
What we ask ourselves is if it is a good idea to do some transformation in the data models to allow us to reuse our functions that we know are working?
Exemple: we have a combobox and we want to manage the display and search. It's always the same and redundant.
This is my class
[Table("stage.Test")]
public partial class Test : IBaseEntity, ICombobox
{
public virtual Product Product { get; set; }
public string nom { get; set; }
public string prenom { get; set; }
public string title { get; set; }
[NotMapped]
public virtual string AffichageCombobox => nom + prenom;
[NotMapped]
public virtual string TexteRecherche => Product.Gabarit.Description;
}
as you can see i have two columns with the tag [NotMapped]. These are the columns in the interface ICombobox
public interface ICombobox
{
string AffichageCombobox { get;}
string TexteRecherche { get; }
}
this is the first service where I use one of my two columns which redirects to other columns. [We use the column "AffichageCombobox" from the model]
public List<ComboboxViewModel> GetComboboxViewModel(int? id, bool actifseulement, string text)
{
var query = _requestDatabaseService.GetComboboxQuery<T>(id, actifseulement, text);
var list = query.Select(table => new ComboboxViewModel
{
Id = table.Id,
AffichageCombobox = table.DateHFin == null ? table.AffichageCombobox : table.AffichageCombobox + " (inactif)"
}).ToList();
return list;
}
This is the RequestDatabaseService [We use the column "TexteRecherche" from the model]
public List<T> GetComboboxQuery<T>(int? id, bool actifseulement, string text) where T : class, IBaseEntity, ICombobox
{
text = text.ToLower();
var list = _dbContext.Set<T>()
.If(id.HasValue,
q => q.Where(x => x.Id == id))
.If(actifseulement,
q => q.Where(x => x.DateHFin == null))
.If(text != "",
q => q.Where(x => x.TexteRecherche.ToLower() == text))
.ToList();
return list;
}
As you can see, I am using an interface to add columns to redirect to the correct columns to my data model to avoid overriding my methods for two column.
Is it a good idea, a good practice ?
What do you think is the best practice if we want to do generic functions, but the columns are not called the same way?
Thank you!
Your solution has a lot of weaknesses
You have extended Model to handle specific UI cases. In my opinion it is bad practice.
Your virtual properties will not work in LINQ query. EF translates only Expression because it canot look into compiled property body.
What we can do here is simplifying of building such comboboxes. I have defind set fo extensions which can be reused for such scenarios. Sorry if there some mistakes, written from memory.
How it can be used:
Assuming that GetComboboxViewModel is not in generic class
public List<ComboboxViewModel> GetComboboxViewModel(int? id, bool actifseulement, string text)
{
// uncover DbContext. All that we need is IQueryable<Test>
var ctx = _requestDatabaseService.GetContext();
var query = ctx.Test.AsQueryable();
var comboItems = query
.FilterItems(id, actifseulement)
.GetComboboxQuery(text, e => e.Product.Gabarit.Description, e => e.nom + e.prenom)
.ToList();
return comboItems;
}
Think about this solution and yes, we can register somewhere pair of Lmbdas Dictionary<Type, (LambdaExpression: searchProp, LambdaExpression: displayProp)> and dynamically build call above.
Realisation:
public static class QueryableExtensions
{
// more simlified version for filtering
public static IQueryable<T> WhereIf(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> predicate)
{
return condition ? query.Where(predicate) : query;
}
// handy function for filtering
public static IQueryable<T> FilterItems<T>(this IQueryable<T> query, int? id, bool onlyActive)
where T : IBaseEntity
{
query = query
.WhereIf(id.HasValue, x => x.Id == id)
.WhereIf(onlyActive, x => x.DateHFin == null)
return query;
}
// dynamic generation of filtering and projection
public static IQueryable<ComboboxViewModel> GetComboboxQuery<T>(this IQueryable<T> query, string text, Expression<Func<T, string>> searchProp, Expression<Func<T, string>> dsiplayProp)
where T : IBaseEntity
{
if (!string.IsNullOrEmpty(text))
{
text = text.ToLower();
// defining search pattern
// this also extension point, you may use here `Contains` or FullText search functions
Expression<Func<string, string, bool>> filterFunc = (s, t) => s.ToLower() == t;
// reusing parameter from searchProp lambda
var param = searchProp.Parameters[0];
// applying pattern to searchprop
var filterBody = ExpressionReplacer.GetBody(filterFunc, searchProp.Body, Expression.Constant(text));
// applying generated filter
var filterPredicate = Expression.Lambda<Func<T, bool>>(filterBody, param);
query = query.Where(filterPredicate);
}
// defining template for Select
Expression<Func<T, string, ComboboxViewModel>> createTemplate = (entity, dp) => new ComboboxViewModel
{
Id = entity.Id,
AffichageCombobox = entity.DateHFin == null ? dp : dp + " (inactif)"
};
// reusing parameter from dsiplayProp lambda
var entityParam = dsiplayProp.Parameters[0];
// injecting dsiplayProp into createTemplate
var selectBody = ExpressionReplacer.GetBody(createTemplate, entityParam, dsiplayProp.Body);
var selectLambda = Expression.Lambda<Func<T, ComboboxViewModel>>(selectBody, entityParam);
// applying projection
var comboQuery = query.Select(selectLambda);
return comboQuery;
}
// helper class for correcting expressions
class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression) lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}
}
Well, after writing this sample, I think, it can be cardinally simplified by using LINQKit. Will post another answer with LINQKit usage if you are interested,
My code is experiencing a problem that I suspect may be self-inflicted. So I should probably answer this question first. Can I/how do I observe a LiveData object that is returned from a Dao that is based on a inner join query and a List parameter?
Unfortunately I do not yet have "10 reputation" on Stackoverflow, so apparently I cannot embed an image. But here is my ERD snapshot as it may help you see how my Entities are tying together: https://i.ibb.co/9YW0Vbx/Screenshot-at-2019-04-06-13-04-43.png
PrayerListFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mTagViewModel = ViewModelProviders.of(this).get(TagViewModel.class);
mPrayerTagViewModel =
ViewModelProviders.of(this).get(PrayerTagViewModel.class);
...
//Update the tag list with the selected tags
mTagViewModel.getSelectedTags().observe(this, new Observer<List<Tag>>() {
#Override
public void onChanged(#Nullable List<Tag> tags) {
if(tags.size() > 0) {
mPrayerTagViewModel.setTagList(tags);
}
}
});
//Observe whatever prayers the view model has to show us
mPrayerTagViewModel.getPrayers().observe(this, new Observer<List<Prayer>>() {
#Override
public void onChanged(#Nullable List<Prayer> prayers) {
mPrayersAdapter.setPrayers(prayers);
}
});
...
}
PrayerTagViewModel
...
private List<String> mTagNames = new ArrayList<>();
...
public LiveData<List<Prayer>> getPrayers() { return getPrayersForTags(mTagNames); }
...
public void setTagList(List<Tag> tags) {
mTagNames = new ArrayList<>();
for (Tag tag: tags) {
mTagNames.add(tag.getName());
}
}
ITagDAO
This returns LiveData objects that I have no trouble observing:
#Query("SELECT * FROM tag_table ORDER BY name")
LiveData<List<Tag>> getAll();
#Query("SELECT * FROM tag_table WHERE selected ORDER BY name")
LiveData<List<Tag>> getSelected();
IPrayerTagDAO
But I am running into issues observing this, so I want to first make sure it is valid syntax:
#Query("SELECT * FROM prayer_table " +
"INNER JOIN prayertag_table " +
"ON summary=fk_summary " +
"WHERE fk_name IN (:names)")
LiveData<List<Prayer>> getPrayersForTags(final List<String> names);
If it is valid syntax, am I possibly losing my observable in my fragment because the call to getPrayers() in PrayerTagViewModel returns a new ViewModel, i.e. a different ViewModel than the one I have started observing in the fragment??
Persistence paid off! I had a sneaky suspicion the mysterious Transformations.switchMap could resolve my issue, but only after a lot more reading did I realize how.
PrayerTagViewModel (modified)
...
private PrayerTagRepository mPrayerTagRepository;
private MutableLiveData<List<String>> mTags = new MutableLiveData<>();
private LiveData<List<Prayer>> mPrayers =
Transformations.switchMap(mTags, mTags -> getPrayersForTags(mTags));
public PrayerTagViewModel(#NonNull Application application) {
super(application);
mPrayerTagRepository = PrayerTagRepository.getRepository(application);
}
...
public LiveData<List<Prayer>> getPrayersForTags(final List<String> names) {
return mPrayerTagRepository.getPrayersForTags(names);
}
public LiveData<List<Prayer>> getPrayers() { return mPrayers; }
...
public void setTagList(List<Tag> tags) {
List<String> tagNames = new ArrayList<>();
for (Tag tag: tags)
tagNames.add(tag.getName());
mTags.setValue(tagNames);
}
PrayersListFragment (modified)
...
//Update the tag list with the selected tags
mTagViewModel.getSelectedTags().observe(this, new Observer<List<Tag>>() {
#Override
public void onChanged(#Nullable List<Tag> tags) {
Log.i(this.getClass().getName(),"onChanged :: Tag size = " + tags.size());
if(tags.size() > 0)
mPrayerTagViewModel.setTagList(tags);
}
});
//Observe whatever prayers the view model has to show us
mPrayerTagViewModel.getPrayers().observe(this,
prayers -> mPrayersAdapter.setPrayers(prayers));
Solution: The switchMap lets my Fragment observe a dynamically changing LiveData (actually a MutableLiveData) from my ViewModel.
Given an object like this:
Matcher matcher = pattern.matcher(sql);
with usage like so:
Set<String> matches = new HashSet<>();
while (matcher.find()) {
matches.add(matcher.group());
}
I'd like to replace this while loop by something more object-oriented like so:
new Iterator<String>() {
#Override
public boolean hasNext() {
return matcher.find();
}
#Override
public String next() {
return matcher.group();
}
}
so that I can easily e.g. make a Stream of matches, stick to using fluent APIs and such.
The thing is, I don't know and can't find a more concise way to create this Stream or Iterator. An anonymous class like above is too verbose for my taste.
I had hoped to find something like IteratorFactory.from(matcher::find, matcher::group) or StreamSupport.of(matcher::find, matcher::group) in the jdk, but so far no luck. I've no doubt libraries like apache commons or guava provide something for this, but let's say I can't use those.
Is there a convenient factory for Streams or Iterators that takes a hasNext/next method combo in the jdk?
In java-9 you could do it via:
Set<String> result = matcher.results()
.map(MatchResult::group)
.collect(Collectors.toSet());
System.out.println(result);
In java-8 you would need a back-port for this, taken from Holger's fabulous answer
EDIT
There is a single method btw tryAdvance that could incorporate find/group, something like this:
static class MyIterator extends AbstractSpliterator<String> {
private Matcher matcher;
public MyIterator(Matcher matcher) {
// I can't think of a better way to estimate the size here
// may be you can figure a better one here
super(matcher.regionEnd() - matcher.regionStart(), 0);
this.matcher = matcher;
}
#Override
public boolean tryAdvance(Consumer<? super String> action) {
while (matcher.find()) {
action.accept(matcher.group());
return true;
}
return false;
}
}
And usage for example:
Pattern p = Pattern.compile("\\d");
Matcher m = p.matcher("12345");
Set<String> result = StreamSupport.stream(new MyIterator(m), false)
.collect(Collectors.toSet());
This class I wrote embodies what I wanted to find in the jdk. Apparently though it just doesn't exist. eugene's accepted answer offers a java 9 Stream solution though.
public static class SearchingIterator<T> implements Iterator<T> {
private final BooleanSupplier advancer;
private final Supplier<T> getter;
private Optional<T> next;
public SearchingIterator(BooleanSupplier advancer, Supplier<T> getter) {
this.advancer = advancer;
this.getter = getter;
search();
}
private void search() {
boolean hasNext = advancer.getAsBoolean();
next = hasNext ? Optional.of(getter.get()) : Optional.empty();
}
#Override
public boolean hasNext() {
return next.isPresent();
}
#Override
public T next() {
T current = next.orElseThrow(IllegalStateException::new);
search();
return current;
}
}
Usage:
Matcher matcher = Pattern.compile("\\d").matcher("123");
Iterator<String> it = new SearchingIterator<>(matcher::find, matcher::group);
i start to use Spring MVC and i have a trouble. I want to get the value of a hashmap with the key. The object ProtoStatus contains a hashmap who i want to get value. I have this error :
org.thymeleaf.exceptions.TemplateProcessingException: Exception
evaluating SpringEL expression: "protoStatus.status.get(30000)"
(template: "protoStatusPage" - line 18, col 21)
public class ProtoStatus
{
public HashMap<String, String> status;
public void computeStatus()
{
this.status = new HashMap();
for (int i=30000; i<30032; i++)
{
this.status.put(String.valueOf(i), String.valueOf(ServerChecker.Check("192.168.0.1", i)));
}
}
public void setStatus(HashMap status)
{
this.status = status;
}
public HashMap getStatus()
{
return this.status;
}
public String getStatus(int key)
{
return (String) this.status.get(key);
}
}
The Spring MVC part :
#PostMapping("/")
public String submit(#ModelAttribute User user, #ModelAttribute ProtoStatus protoStatus)
{
protoStatus = new ProtoStatus();
protoStatus.computeStatus();
return "protoStatusPage";
}
And Finaly, in the template protoStatusPage.html, i want to get the value for key 30000:
<p th:text="${protoStatus.status.get(30000)}" />
Your status attribute is null by default. It only becomes non-null when you call computeStatus(). But you're never calling it on the ProtoStatus used by the view:
protoStatus = new ProtoStatus();
protoStatus.computeStatus();
Instead, you create a different one, and call computeStatus() on that one, that is then immediately eligible to garbage collection.
Also, you have a Map<String, String>, but your getStatus() method, and the expression in your view, tries to get something out of it usig a key of type Integer. That will always return null.
Your HashMap<String,String> contains both the String object. So you have to call using
Replace this line
<p th:text="${protoStatus.status.get(30000)}" />
with
<p th:text="${protoStatus.status.get('30000')}" />
I have following error in this code: Cannot infer type arguments for ReadOnlyListWrapper<>
How should my return type look like? I need to save arraylist for each node in all columns. But I can not return it.
for (Entry<String, String> ent : dc.getSortedOrgAll().entrySet()) {
TreeTableColumn<String, ArrayList<String>> col = new TreeTableColumn<>(
ent.getValue());
col.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<String, ArrayList<String>>, ObservableValue<ArrayList<String>>>() {
#Override
public ObservableValue<ArrayList<String>> call(
CellDataFeatures<String, ArrayList<String>> param) {
TreeMap<String, List<String>> temp = (TreeMap<String, List<String>>) dc
.getFuncTypeOrg().clone();
ArrayList<String> result = new ArrayList<>();
for (int i = 0; i < temp.size(); i++) {
List<String> list = temp.firstEntry().getValue();
String key = temp.firstEntry().getKey();
// root.getChildren();
if (list.get(1).equals("Papier")) {
System.out.println(list.get(1));
}
if (list.get(1).equals(param.getValue().getValue())
&& list.get(5).equals(col.getText())) {
result.add(list.get(2));
if(list.size()==9)
result.add(list.get(list.size()-1));
else result.add("White");
} else {
temp.remove(key);
// result = null;
}
}
return new ReadOnlyListWrapper<>(result);
}
});
ReadOnlyListWrapper<T> implements ObservableValue<ObservableList<T>>, which isn't what you need, as you declared the callback to return an ObservableValue<ArrayList<T>> (T is just String here).
So I think you just need
return new ReadOnlyObjectWrapper<ArrayList<String>>(result);
and you can probably omit the generic type:
return new ReadOnlyObjectWrapper<>(result);
Just a comment: I think you could make your life much easier by defining some actual data model classes, instead of trying to force your data into various collections implementations.