Iterate over map and run method on each result - APEX - java

I'm looking to check the field level security of all of the fields on the Opportunity object.
So I'm getting the full map of fields and storing them in a static variable so I can access them from where ever I need.
private static Map<String, Schema.SObjectField> myMap;
private static void initMaps() {
myMap = Schema.SObjectType.Opportunity.fields.getMap();
}
I want to iterate over all of the fields on the object and check .isAccessible() on each.
I'm a little stuck as to how to iterate over the fields however, and also how to check .isAccessible() on eash.
//THIS BIT DOESN'T WORK...
private doCheckMap(myMap){
for (Id key : myMap.keySet()) {
if(false == Schema.SObjectType.???.fields.isAccessible()) {
System.debug('nope!');
}
}
}
Any suggestions or advice would be greatly appreciated.
Cheers.

You could iterate through yout map like this:
Map<String, MyType> myMap = new HashMap<String, MyType>();
Iterator<Map.Entry<String, MyType>> iter = myMap.entrySet().iterator();
while(iter.hasNext()){
MyType entry = iter.next().getValue();
for (Field f : entry.getClass().getFields()) {
if (f.isAccessible()) {
//...
}
}
}
Then again why do you even need a map? Couldn'you just use reflexion api getFields() - like this:
for (Field f : Opportunity.getClass().getFields()) {
if (f.isAccessible()) {
//...
}
}

Related

Improve performance of mapping values

I have the following code. I would like to know if I can improve the performance of the following code.
class CarModel {
private long carKey;
private String carName;
private CarColor carColor;
private CarEngine carEngine;
}
class CarColor {
private long carKey;
private String carPrimaryColor;
private String carSecondaryColor;
}
class CarEngine {
private long carKey;
private String carEnginePartNumber;
private String carEngineTransmissionFluid;
}
I am getting values for CarModel class from database which has only carKey and carName.
Then I get values for CarColor and carEngine and perform some data transformation on those lists and then assign the values to the CarModel so that I can update tables on the database.
The mapping of values to the CarModel is done in the following way
private void addList(List<?> list, List<CarModel> carModelList) {
carModelList.stream().forEach(x -> {
for (Object object : list) {
if (object instanceof CarColor
&& ((CarColor) object).getCarKey() == x.getCarKey()) {
x.setCarColor(((CarColor) object));
break;
}
if (object instanceof CarEngine
&& ((CarEngine) object).getCarKey() == x.getCarKey()) {
x.setCarEngine(((CarEngine) object));
break;
}
}
});
}
The above code works. Is there any way I can improve the code in addList() method and make it run faster?
Any help would be greatly appreciated! Thanks you
Yes, by storing the CarColor and CarEngine objects in a Map data structure with their carKey as the key.
Then, rather than repeatedly checking the list, the necessary CarColor or CarEngine object from the Map may be retrieved to perform the mapping of values to the CarModel.
This would significantly lower the time complexity from O(n2) to O(n).
Something like:
void addList(List<?> list, List<CarModel> carModelList) {
Map<Long, CarColor> colorMap = new HashMap<>();
Map<Long, CarEngine> engineMap = new HashMap<>();
for (Object object : list) {
if (object instanceof CarColor) {
CarColor color = (CarColor) object;
colorMap.put(color.getCarKey(), color);
}
if (object instanceof CarEngine) {
CarEngine engine = (CarEngine) object;
engineMap.put(engine.getCarKey(), engine);
}
}
carModelList.forEach(x -> {
CarColor color = colorMap.get(x.getCarKey());
if (color != null) {
x.setCarColor(color);
}
CarEngine engine = engineMap.get(x.getCarKey());
if (engine != null) {
x.setCarEngine(engine);
}
});
}
For each element of carModelList you iterate the complete list once, meaning you'll have n * m inner loop iterations where n is the size of carModelList and m is the size of list.
Instead of looping over carModelList, it would be much faster to build a Map<Long,CarModel> (that you probably want to keep around in the long term, possibly instead of carModelList) and just lookup the CarModel object using the objects carKey, thus eliminating the outer loop entirely.
One way to improve the performance of this code is to use a Map instead of a List to store the information in the "list" parameter. The key of the map could be the "carKey" and the value could be either a "CarColor" or a "CarEngine". Then, you could use the "get" method of the map to retrieve the information for each "CarModel" in the "carModelList". This would eliminate the need to iterate through the entire list for each "CarModel".
Here's the updated code:
private void addList(Map<Integer, Object> map, List<CarModel> carModelList) {
carModelList.stream().forEach(x -> {
Object object = map.get(x.getCarKey());
if (object instanceof CarColor) {
x.setCarColor((CarColor) object);
} else if (object instanceof CarEngine) {
x.setCarEngine((CarEngine) object);
}
});
}

How to access an unknown enum's "valueOf()" method

I have configuration files which can be populated with enums and their respective values and will then be read by my program.
For example, a configuration file (yaml format) may look like this:
SomeEnumClass:
- VALUE_A_OF_SOME_ENUM
- VALUE_B_OF_SOME_ENUM
- ANOTHER_VALUE
AnotherEnumClass:
- VALUE_1
- VALUE_3
- VALUE_3
- VALUE_7
[etc...]
Unfortunately this leads to duplication in my code (java) like this:
if (enumNameString.equals("SomeEnumClass")) {
Collection<SomeEnumClass> values = new ArrayList<>;
for (String listEntry : yamlConfig.getStringList(enumNameString)) {
values.add(SomeEnumClass.valueOf(listEntry));
}
return values;
} else if (enumNameString.equals("AnotherEnumClass")) {
Collection<AnotherEnumClass> values = new ArrayList<>;
for (String listEntry : yamlConfig.getStringList(enumNameString)) {
values.add(AnotherEnumClass.valueOf(listEntry));
}
return values;
} else if ...
} else if ...
} else if ...
(please keep in mind that this example is pseudo code)
So of course i'm trying to get rid of the duplicate code. But how?
Is it possible to:
Get a class from a string? ("SomeEnumClass" -> SomeEnumClass.class)
Then check if that class is castable to Enum or something?
Access the enum's valueOf() method from that cast?
As usual, all things reflection are typically evil. However to get all the enum constants for a fully named class:
Class.forName(enumNameString).getEnumConstants()
<T extends Enum<T> Enum.valueOf(Class<T>,String) is great, but I don't know of a reasonable, obviously safe way to narrow a Class<?> to a Class<T extends Enum<T>> (Class.asSubclass will get you as far as Class<T extends Enum>).
Slightly better it to switch (or keep a Map) onto available constants:
Enum<?>[] values = switch (enumNameString) {
case "SomeEnumClass" -> SomeEnumClass .values();
case "AnotherEnumClass" -> AnotherEnumClass.values();
default -> throw new Error();
};
Enum<?> en = Arrays.stream(values)
.filter(e -> e.name() == listEntry).findFirst().get();
If building a Map derived from classes, it may be easier to use EnumSet.allOf(Class<?>) than MyEnum.values(), Enum.valueOf() or Class.getEnumConstants()
You can create a Map<String, Class<?>> which contains the mapping like this:
private static final Map<String, Class<Enum<?>>> MAP;
static {
Map<String, Class<Enum<?>>> map = new HashMap<>();
map.put(SomeEnumClass.class.getSimpleName(), SomeEnumClass.class);
// your other enum classes
MAP = Collections.unmodifiableMap(map);
}
And then you can make use of Enum.valueOf(Class<Enum>, String):
Class<Enum<?>> enumClass = MAP.get(enumNameString);
if (enumClass != null) {
Collection<Enum<?>> values = new ArrayList<>;
for (String listEntry : yamlConfig.getStringList(enumNameString)) {
values.add(Enum.valueOf(enumClass, listEntry));
}
return values;
}

Iterate through the list and grouping based on dates

I am trying to achieve, where I am looking for ways to group the list based on the dates. I have a lost of attribute as below:
List<Attribute> attributes
where
Attribute.java is as below
public class Attribute
{
Integer getValue();
List<String> getString();
Date getDate();
}
I am looking for a way such that , while iterating through the list of Attribute , I could create a List of elements (current date) and map of elements based on the dates (in the past) having the same IntegerValue.
My codes goes as below:
List<Attribute> currentElement = new ArrayList<Attribute>();
Map<Integer, List<Attribute>> historicalElement = new HashMap<Integer, List<Attribute>>();
//iterating the entire list
for(final Attribute attribute : attributes)
{
if(attribute.getDate() == currentDate)
{
currentElement.add(attribute);
}
if(attribute.getDate() < currentDate)
{
historicalElement.put(attribute.getValue(), attribute)
}
}
The statement
historicalElement.put(attribute.getValue(), attribute)
wont work beacuse
The method put(Integer, List<Attribute>) in the type Map<Integer,List<Attribute>> is not applicable for the arguments (Integer, Attribute).
Is there any way that we can achieve that map , rather than typing casting to List.
Thanks !!!
Casting to list won't help at all. You'll only get a ClassCastException. Simplest way might be this:
if(attribute.getDate() < currentDate)
{
List<Attribute> list = historicalElement.get(attribute.getValue());
if(list == null){
list = new ArrayList<>();
historicalElement.put(attribute.getValue() , list);
}
list.add(attribute);
}
edit: Paul's answer is better here.
Looks like a job for guava multimap where you can do:
Map<Integer, List<Attribute>> historicalElement = Multimaps.newListMultimap();
for(final Attribute attribute : attributes) {
historicalElement.put(attribute.getValue(), attribute)
}
should do it.
Well, except that you want to group by the date as well? That's slightly trickier.
Firstly you need to fix your date comparison. You don't compare dates using == operator.
Now while adding a new entry in map, you've to first check the existing key. If not there then create a new ArrayList with new value:
if(attribute.getDate().compareTo(currentDate) < 0) {
if (historicalElement.containsKey(attribute.getValue())) {
historicalElement.get(attribute.getValue()).add(attribute);
} else {
historicalElement.put(attribute.getValue(),
new ArrayList<Attribute>(Arrays.asList(attribute)));
}
}
If you're using Java 8, you can directly use Map#merge() method to avoid that extra testing:
if(attribute.getDate().compareTo(currentDate) < 0) {
historicalElement.merge(
attribute.getValue(),
new ArrayList<Attribute>(Arrays.asList(attribute)),
ArrayList::addAll);
}
You can also use Stream API and lambda here:
List<Attribute> currentElement = attributes.stream()
.filter(a -> a.getDate().compareTo(currentDate) == 0)
.collect(Collectors.toList());
Map<Integer, List<Attribute>> historicalElement = attributes.stream()
.filter(a -> a.getDate().compareTo(currentDate) < 0)
.collect(Collectors.groupingBy(Attribute::getValue));
Aren’t you trying to put a single Attribute in a Map where it requires a List of Attributes, here:
//iterating the entire list
for(final Attribute attribute : attributes)
{
if(attribute.getDate() == currentDate)
{
currentElement.add(attribute);
}
if(attribute.getDate() < currentDate)
{
historicalElement.put(attribute.getValue(), attribute) // HERE
}
}
If you want it to be single attribute, this should change:
From: Map<Integer, List<Attribute>> historicalElement = new HashMap<Integer, List<Attribute>>();
To: Map<Integer, List<Attribute>> historicalElement = new HashMap<Integer, Attribute>();

prepare a multi valued country map

I want to prepare a HashMap in such way that
Key : Country Code
Value : List of returned orderEntries
the following process data method process every 5 orderEntry which can be from any country.
let me make it more clear. I have list of orderEntries that come from different countries now I want to put these entries into map based on country key. Like if 20 entries coming from US then US will be the key and 20 Entries would be the values. But problem is that I don't want to create a list for each county inside map.
public void processSegmentData(final List resultSet)
{
for (final Object orderEntry : resultSet)
{
if (orderEntry instanceof OrderEntryModel)
{
String countryCode = null;
final OrderModel order = ((OrderEntryModel) orderEntry).getOrder();
if (order.getDeliveryAddress() != null)
{
countryCode = order.getDeliveryAddress().getCountry().getIsocode();
}
orderEntriesMap.put(Config.getParameter(countryCode+".return.pid"), orderEntries);
}
}
}
so you are after a hashmap which contains a linked list Something along the lines of:
public HashMap<String, LinkedList<OrderEntryModel>> processSegmentData(final List resultSet) {
HashMap<String, LinkedList<OrderEntryModel>> orderEntriesMap = new HashMap<String, LinkedList<OrderEntryModel>>();
for (final Object orderEntry : resultSet) {
if (orderEntry instanceof OrderEntryModel) {
String countryCode = null;
final OrderModel order = ((OrderEntryModel) orderEntry).getOrder();
if (order.getDeliveryAddress() != null) {
countryCode = order.getDeliveryAddress().getCountry().getIsocode();
}
if (!orderEntriesMap.containsKey(countryCode)) {
orderEntriesMap.put(countryCode, new LinkedList<OrderEntryModel>());
}
orderEntriesMap.get(countryCode).add((OrderEntryModel) orderEntry);
}
}
return orderEntriesMap;
}
would be an example based on the source code you provided guessing object names.
But problem is that I don't want to create a list for each county
inside map.
I understand your problem but map store unique key, you can not store same country code.
you have to use Map<String, List<String>>() that will hold your country code as key and then put your values inside List<String>.
after doing this if you have any problem edit your question will help you to resolve that.
Just Create a Map<String,List<String>>. and follow the following approach
Map<String,List<String>> countryMap = new HashMap<String, List<String>>();
for (final String orderEntry : orders){
if(countryMap.containsKey(orderEntry.getCountry())){
countryMap.get(orderEntry.getCountry()).add(orderEntry);
}else{
//create a new list and add orderEntry
countryMap.put(orderEntry.getCountry(),orderEntry);
}
}
You need to modify this according to your stuff
You could use Guava's Multimap to simplify things. A Multimap allows you to store multiple entries against a single key, e.g.:
Multimap<String, OrderEntry> orderEntriesMultimap = HashMultimap.create();
for (final Object orderEntry : resultSet) {
// omitted...
orderEntriesMultimap.put(Config.getParameter(countryCode+".return.pid"), orderEntry);
}
You can then retrieve all the associated values by key:
Collection<OrderEntryModel> entries = orderEntriesMultimap.get(key);

Get key from a HashMap using the value [duplicate]

This question already has answers here:
Java Hashmap: How to get key from value?
(39 answers)
Closed 5 years ago.
I want to get the key of a HashMap using the value.
hashmap = new HashMap<String, Object>();
haspmap.put("one", 100);
haspmap.put("two", 200);
Which means i want a function that will take the value 100 and will return the string one.
It seems that there are a lot of questions here asking the same thing but they don't work for me.
Maybe because i am new with java.
How to do it?
The put method in HashMap is defined like this:
Object put(Object key, Object value)
key is the first parameter, so in your put, "one" is the key. You can't easily look up by value in a HashMap, if you really want to do that, it would be a linear search done by calling entrySet(), like this:
for (Map.Entry<Object, Object> e : hashmap.entrySet()) {
Object key = e.getKey();
Object value = e.getValue();
}
However, that's O(n) and kind of defeats the purpose of using a HashMap unless you only need to do it rarely. If you really want to be able to look up by key or value frequently, core Java doesn't have anything for you, but something like BiMap from the Google Collections is what you want.
We can get KEY from VALUE. Below is a sample code_
public class Main {
public static void main(String[] args) {
Map map = new HashMap();
map.put("key_1","one");
map.put("key_2","two");
map.put("key_3","three");
map.put("key_4","four");
System.out.println(getKeyFromValue(map,"four"));
}
public static Object getKeyFromValue(Map hm, Object value) {
for (Object o : hm.keySet()) {
if (hm.get(o).equals(value)) {
return o;
}
}
return null;
}
}
I hope this will help everyone.
If you need only that, simply use put(100, "one"). Note that the key is the first argument, and the value is the 2nd.
If you need to be able to get by both the key and the value, use BiMap (from guava)
You have it reversed. The 100 should be the first parameter (it's the key) and the "one" should be the second parameter (it's the value).
Read the javadoc for HashMap and that might help you: HashMap
To get the value, use hashmap.get(100).
You mixed the keys and the values.
Hashmap <Integer,String> hashmap = new HashMap<Integer, String>();
hashmap.put(100, "one");
hashmap.put(200, "two");
Afterwards a
hashmap.get(100);
will give you "one"
if you what to obtain "ONE" by giving in 100 then
initialize hash map by
hashmap = new HashMap<Object,String>();
haspmap.put(100,"one");
and retrieve value by
hashMap.get(100)
hope that helps.
public class Class1 {
private String extref="MY";
public String getExtref() {
return extref;
}
public String setExtref(String extref) {
return this.extref = extref;
}
public static void main(String[] args) {
Class1 obj=new Class1();
String value=obj.setExtref("AFF");
int returnedValue=getMethod(value);
System.out.println(returnedValue);
}
/**
* #param value
* #return
*/
private static int getMethod(String value) {
HashMap<Integer, String> hashmap1 = new HashMap<Integer, String>();
hashmap1.put(1,"MY");
hashmap1.put(2,"AFF");
if (hashmap1.containsValue(value))
{
for (Map.Entry<Integer,String> e : hashmap1.entrySet()) {
Integer key = e.getKey();
Object value2 = e.getValue();
if ((value2.toString()).equalsIgnoreCase(value))
{
return key;
}
}
}
return 0;
}
}
If you are not bound to use Hashmap, I would advise to use pair< T,T >.
The individual elements can be accessed by first and second calls.
Have a look at this http://www.cplusplus.com/reference/utility/pair/
I used it here : http://codeforces.com/contest/507/submission/9531943

Categories

Resources