Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sweep: please develop a class that implements a map like data structure with a set of keys and a list of values that are linked. #123

Open
2 tasks done
codefriar opened this issue Oct 19, 2023 · 2 comments · May be fixed by #124
Labels
enhancement New feature or request sweep Assigns Sweep to an issue or pull request.

Comments

@codefriar
Copy link
Owner

codefriar commented Oct 19, 2023

Summary

The map implementation should adhere to the limitations of a Apex defined type.

Suggested Solution

No response

Alternative Solutions

No response

Checklist
  • force-app/main/default/classes/LinkedMap.cls ✅ Commit 5364b7f
  • force-app/main/default/classes/LinkedMapTests.cls ✅ Commit cddce6e
@codefriar codefriar added the enhancement New feature or request label Oct 19, 2023
@sweep-ai sweep-ai bot added the sweep Assigns Sweep to an issue or pull request. label Oct 19, 2023
@sweep-ai
Copy link

sweep-ai bot commented Oct 19, 2023

Here's the PR! #124.

⚡ Sweep Basic Tier: I'm creating this ticket using GPT-4. You have 5 GPT-4 tickets left for the month and 3 for the day. For more GPT-4 tickets, visit our payment portal.

Actions (click)

  • ↻ Restart Sweep

Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I looked at (click to expand). If some file is missing from here, you can mention the path in the ticket description.

/**
* @description This class is full of methods that *I* think should be in the Apex language, but aren't. It's
* opinionated, and these represent the best solutions i've found to the gnarly problems i've come across.
*/
public with sharing class Polyfills {
/**
* @description Class exists to reserve an object type that cannot be instantiated. It is used for generating stack
* traces on demand, and other reflection operations.
*/
private class GuaranteedNotToExist {
}
/**
* @description Exception used internally to throw exceptions that
* are intentional and used for unofficial reflection
* operations.
*/
private class GuaranteedNotToExistException extends Exception {
}
/**
* @description Used to determine what the Class name
* of the passed in Object is. There are many mostly
* accurate ways of doing this, but this is the only
* one that works in all cases.
*
* @param obj Object the object whose class name you want
*
* @return String the name of the class of the passed in object.
*/
public static String classNameFromInstance(Object obj) {
String result = '';
try {
GuaranteedNotToExist shouldThrow = (GuaranteedNotToExist) obj; //NOPMD
} catch (System.TypeException expectedException) {
result = expectedException
.getMessage()
.toLowerCase()
.substringBetween(
'invalid conversion from runtime type ',
' to polyfills.guaranteednottoexist'
);
}
return result;
}
/**
* @description Method returns a Type object from an object instance. This is useful for reflection operations
*
* @param obj The object to get the type of
*
* @return Type the type of the passed in object
*/
public static Type typeObjFromInstance(Object obj) {
return Type.forName(classNameFromInstance(obj));
}
/**
* @description Method determines the type of a list from it's first element.
* This is potentially incorrect, if your List is defined `List<SObject>` rather than `List<Account>`
* or some other specific SObject
*
* @param sObjects A list of SObjects
*
* @return String the name of the SObject type of the first element in the list
*/
public static String getSObjectTypeFromListsFirstObject(
List<SObject> sObjects
) {
return (!sObjects.isEmpty())
? sObjects.get(0).getSObjectType().getDescribe().getName()
: 'sObject';
}
/**
* @description Method is responsible for building a map out of a list where you can specify the key. This is
* useful for drying up your code, as generating maps by a non-record-id key is ... common.
*
* Note: you'll need to cast this on the calling side.
*
* @param key String the name of the field to use as the key. ** This must be an ID field **
* However, it doesn't have to be the record id. It can be any field that is an ID field.
* @param incomingList List<SObject> the list of SObjects to build the map from
*
* @return Map<Id, SObject> the map of the passed in list, keyed by the passed in key
*/
public static Map<Id, SObject> idMapFromCollectionByKey(
String key,
List<SObject> incomingList
) {
String objType = getSObjectTypeFromListsFirstObject(incomingList);
Type dynamicMapType = Type.forName('Map<Id,' + objType + '>');
Map<Id, SObject> returnValues = (Map<Id, SObject>) dynamicMapType.newInstance();
for (SObject current : incomingList) {
if (current.get(key) != null) {
returnValues.put((Id) current.get(key), current);
}
}
return returnValues;
}
/**
* @description Method is responsible for building a map out of a list where you can specify the key. This is
* useful for drying up your code, as generating maps by a non-record-id key is ... common.
*
* Note: you'll need to cast this on the calling side.
*
* @param key String the name of the field to use as the key. ** This must be an STRING field **
* @param incomingList List<SObject> the list of SObjects to build the map from
*
* @return Map<Id, SObject> the map of the passed in list, keyed by the passed in key
*/
public static Map<String, SObject> stringMapFromCollectionByKey(
String key,
List<SObject> incomingList
) {
String objType = getSObjectTypeFromListsFirstObject(incomingList);
Type dynamicMapType = Type.forName('Map<String,' + objType + '>');
Map<String, SObject> returnValues = (Map<String, SObject>) dynamicMapType.newInstance();
for (SObject current : incomingList) {
if (current.get(key) != null) {
returnValues.put((String) current.get(key), current);
}
}
return returnValues;
}
/**
* @description This method is responsible for building a map out of a list where you can specify the key. However
* this method is designed to help you group records by common keys. For instance, you can use this method to group
* a list of contacts by their accountIds by passing in 'AccountId' as the key.
*
* Note: you'll need to cast this on the calling side. The key used here must be an ID field.
*
* @param key String the name of the field to use as the key. ** This must be an ID field **
* @param incomingList List<SObject> the list of SObjects to build the map from
*
* @return Map<Id, List<SObject>> the map of the passed in list, grouped by the passed in key
*/
public static Map<Id, List<SObject>> mapFromCollectionWithCollectionValues(
String key,
List<SObject> incomingList
) {
String objType = getSObjectTypeFromListsFirstObject(incomingList);
Type listObjType = Type.forName('List<' + objType + '>');
Type dynamicMapType = Type.forName('Map<Id, List<' + objType + '>>');
Map<Id, List<SObject>> returnValues = (Map<Id, List<SObject>>) dynamicMapType.newInstance();
for (SObject current : incomingList) {
if (current.get(key) != null) {
if (returnValues.keySet().contains((Id) current.get(key))) {
List<SObject> existingList = (List<SObject>) returnValues.get(
(Id) current.get(key)
);
existingList.add(current);
returnValues.put((Id) current.get(key), existingList);
} else {
List<SObject> newList = (List<SObject>) listObjType.newInstance();
newList.add(current);
returnValues.put((Id) current.get(key), newList);
}
}
}
return returnValues;
}
/**
* @description This method will give you a stack trace you can inspect. It's useful for debugging, and for things
* like determining the call stack of a method.
*
* @return String The stack trace of the current execution context.
*/
public static String generateStackTrace() {
return new DmlException().getStackTraceString();
}
/**
* @description Similar to the pluck method in lodash, this method will return a list of strings from a list of
* SObjects, based on the field name you pass in.
*
* @param fieldName String the name of the field to 'pluck'
* @param incomingList List<SObject> list of objects to pluck from
*
* @return List<String> list containing the string value of the field you passed in from every record in the
* incoming list
*/
public static List<String> pluckFieldFromList(
String fieldName,
List<SObject> incomingList
) {
List<String> returnValues = new List<String>();
for (SObject current : incomingList) {
returnValues.add(String.valueOf(current.get(fieldName)));
}
return returnValues;
}
/**
* @description Well, as much as I'd like to make this a generic method, I can't
* Apex doesn't provide a way to dynamically cast a list of one type
* to another type. So, this is a method that will only work for Ids
* Future versions of this class might include methods of the same
* name but with different parameters to handle other types
*
* This makes me sad.
*
* @param setToCheck A set of strings to check
* @param listOfPossibleOptions List<String> a list of strings that might be in the set
*
* @return Boolean True if any of the strings in the list are in the set
*/
public static Boolean setContainsAnyItemFromList(
Set<String> setToCheck,
List<String> listOfPossibleOptions
) {
Boolean setContainsAnyElementOfList = false;
for (String current : listOfPossibleOptions) {
if (setToCheck.contains(current)) {
setContainsAnyElementOfList = true;
break;
}
}
return setContainsAnyElementOfList;
}
/**
* @description Generates a UUIDv4 string. This is useful for generating unique identifiers for things.
*
* @return String a UUIDv4 string
*/
public static String generateUUID() {
Blob aesKey = Crypto.generateAesKey(128);
String hexEncodedKey = EncodingUtil.convertToHex(aesKey);
String guid =
hexEncodedKey.substring(0, 8) +
'-' +

https://github.com/codefriar/ApexKit/blob/b36452fdd4e4fdc5776e203db4738a7167b6534c/force-app/main/default/classes/test utilities/HttpCalloutMockFactory.cls#L1-L73

5,
true
);
for (Account acct : accounts) {
acct.name = acct.id;
}
update accounts;
Test.startTest();
Map<String, Account> checkAccountMap = (Map<String, Account>) Polyfills.stringMapFromCollectionByKey(
'name',
accounts
);
Test.stopTest();
Assert.areEqual(
checkAccountMap.keySet().size(),
5,
'Expected to get 5 accounts back'
);
for (id accountId : checkAccountMap.keySet()) {
Assert.areEqual(
checkAccountMap.get(accountId).getSObjectType(),
Account.getSObjectType(),
'We expected the map to have accounts'
);
}
}
@isTest
private static void testMapFromCollectionWithListOfValuesPostive() {
List<Account> accounts = (List<Account>) TestFactory.createSObjectList(
new Account(),
5,
true
);
List<Contact> contactList = new List<Contact>();
for (Account acct : accounts) {
contactList.addAll(
(List<Contact>) TestFactory.createSObjectList(
new Contact(accountId = acct.id),
5,
false
)
);
}
insert contactList;
Test.startTest();
Map<Id, List<Contact>> checkResults = (Map<Id, List<Contact>>) Polyfills.mapFromCollectionWithCollectionValues(
'AccountId',
contactList
);
Test.stopTest();
Assert.areEqual(
5,
checkResults.keyset().size(),
'Expected to find 5 accountIds'
);
for (Id accountId : checkResults.keySet()) {
Assert.areEqual(
accountId.getSobjectType(),
Account.getSObjectType(),
'Expected keys to be accounts'
);
Assert.areEqual(
5,
checkResults.get(accountId).size(),
'Expected to find 5 entries in the list '
);
for (Contact contact : checkResults.get(accountId)) {
Assert.areEqual(
contact.getSObjectType(),
Contact.getSObjectType(),
'Expected to find contacts'
);
}

https://github.com/codefriar/ApexKit/blob/b36452fdd4e4fdc5776e203db4738a7167b6534c/force-app/main/default/classes/feature flags/FeatureFlagDataProvider.cls#L185-L247


Step 2: ⌨️ Coding

  • force-app/main/default/classes/LinkedMap.cls ✅ Commit 5364b7f
Create force-app/main/default/classes/LinkedMap.cls with contents:
• Create a new class `LinkedMap.cls` in the `force-app/main/default/classes/` directory.
• In the `LinkedMap.cls` class, define a private variable `linkedMap` of type `Map, List>`.
• Implement a constructor that initializes the `linkedMap` variable.
• Implement a method `put(Set keys, List values)` that adds a new entry to the `linkedMap`. This method should check if the keys already exist in the map. If they do, it should append the values to the existing list. If they don't, it should add a new entry to the map.
• Implement a method `get(Set keys)` that returns the list of values associated with the given keys. This method should return null if the keys do not exist in the map.
• Implement a method `remove(Set keys)` that removes the entry with the given keys from the map. This method should return the list of values that were associated with the keys, or null if the keys did not exist in the map.
• Implement a method `containsKey(Set keys)` that checks if the map contains an entry with the given keys. This method should return true if the keys exist in the map, and false otherwise.
• Implement a method `keySet()` that returns the set of all keys in the map.
• Implement a method `values()` that returns a list of all values in the map.


Sandbox logs for 5364b7f
trunk fmt `force-app/main/default/classes/LinkedMap.cls` 1/2 ✓
Found no applicable linters for the requested path
trunk check --fix --print-failures `force-app/main/default/classes/LinkedMap.cls` 2/2 ✓



Checked 1 file
✔ No issues
  • force-app/main/default/classes/LinkedMapTests.cls ✅ Commit cddce6e
Create force-app/main/default/classes/LinkedMapTests.cls with contents:
• Create a new class `LinkedMapTests.cls` in the `force-app/main/default/classes/` directory.
• In the `LinkedMapTests.cls` class, implement test methods for each method in the `LinkedMap.cls` class. These test methods should cover all possible edge cases, including but not limited to: adding entries to the map, retrieving values from the map, removing entries from the map, checking if keys exist in the map, and retrieving all keys and values from the map.


Sandbox logs for cddce6e
trunk fmt `force-app/main/default/classes/LinkedMapTests.cls` 1/2 ✓
Found no applicable linters for the requested path
trunk check --fix --print-failures `force-app/main/default/classes/LinkedMapTests.cls` 2/2 ✓




Checked 1 file
✔ No issues

Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/linked-map-implementation.

.


🎉 Latest improvements to Sweep:

  • Sweep can now passively improve your repository! Check out Rules to learn more.

💡 To recreate the pull request edit the issue title or description. To tweak the pull request, leave a comment on the pull request.
Join Our Discord

@github-actions
Copy link

Thank you for posting this issue. 🙇🏼‍♂️
We will get back to you shortly.

@sweep-ai sweep-ai bot linked a pull request Oct 19, 2023 that will close this issue
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request sweep Assigns Sweep to an issue or pull request.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant