Month: February 2020
How to parse an Object that has no keys in Java
I have the following query:
public List<Object> findNearbyGangs(double lat, double lng, double distance) {
Query query = this.entityManager.createNativeQuery("SELECT id, (6371 * acos (cos(radians(:latitude)) * cos(radians(latitude)) * cos(radians(longitude) - radians(:longitude)) + sin(radians(:latitude)) * sin(radians(latitude)))) AS distance FROM Gang g GROUP BY id HAVING distance < :distance ORDER BY distance")
.setParameter("latitude", lat)
.setParameter("longitude", lng)
.setParameter("distance", distance);
List<Object> objects = query.getResultList();
return objects;
}
which objects contain:
[
[
3,
0
],
[
321,
0
],
[
353,
1.3575295456440253
]
]
How do I parse this object so that id = 3 and distance = 0, id = 321 and distance = 0, and so on?
I asked on Stackoverflow but got no response so I ended up figuring it out myself:
public List<NearbyGang> getNearbyGangs(Double longitude, Double latitude, int radius) {
List<Object> nearbyGangsFromDatabase = this.gangRepositoryImpl.findNearbyGangs(longitude, latitude, radius);
List<NearbyGang> nearbyGangs = new ArrayList<NearbyGang>();
for (Object object : nearbyGangsFromDatabase) {
Class oClass = object.getClass();
if (oClass.isArray()) {
for (int i = 0; i < Array.getLength(object); i++) {
BigInteger gangId = (BigInteger) Array.get(object, 0);
Gang gang = this.getById(gangId.longValue()).get();
Double distance = (Double) Array.get(object, 1);
NearbyGang nearbyGang = new NearbyGang(gang, distance);
nearbyGangs.add(nearbyGang);
}
}
}
return nearbyGangs;
}
Improvements welcomed.
Refactoring Multi-Dimensional Array PHP to Object Oriented PHP
One of my favourite things to do is refactor legacy code. I get to see the thinking process that went into solving the problem. It’s like standing on the shoulders of giants.
private function convertActivityToArray(array $activity)
{
$activityArray = array();
$previousGroupNumber = null;
/** @var Activity $activityItem */
foreach ($activity as $activityItem) {
$type = $activityItem->getType();
$groupNumber = $activityItem->getGroupNumber();
$user = $activityItem->getUser();
$name = $user->getName();
$avatar = $this->request->getScheme() .'://' . $this->request->getHttpHost() . $this->request->getBasePath() . '/uploads/avatar/' . $user->getAvatar();
$data = $activityItem->getData();
$created = $activityItem->getCreatedAt();
$activityArray[$groupNumber]['type'] = $type;
$activityArray[$groupNumber]['created'] = $created;
if ($type == ActivityType::Like) {
if ($groupNumber != $previousGroupNumber) {
$data['documentaryThumbnail'] = $this->request->getScheme() .'://' . $this->request->getHttpHost() . $this->request->getBasePath() . '/uploads/posters/' . $data['documentaryThumbnail'];
$activityArray[$groupNumber]['parent']['data'] = $data;
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
} else {
$data['documentaryThumbnail'] = $this->request->getScheme() .'://' . $this->request->getHttpHost() . $this->request->getBasePath() . '/uploads/posters/' . $data['documentaryThumbnail'];
$child['data'] = $data;
$child['user']['name'] = $name;
$child['user']['avatar'] = $avatar;
$activityArray[$groupNumber]['child'][] = $child;
}
} else if ($type == ActivityType::Comment) {
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
$activityArray[$groupNumber]['parent']['data'] = $data;
} else if ($type == ActivityType::Joined) {
if ($groupNumber != $previousGroupNumber) {
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
} else {
$child['user']['name'] = $name;
$child['user']['avatar'] = $avatar;#
$activityArray[$groupNumber]['child'][] = $child;
}
} else if ($type == ActivityType::Added) {
if ($groupNumber != $previousGroupNumber) {
$activityArray[$groupNumber]['parent']['data'] = $data;
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
} else {
$child['data'] = $data;
$child['user']['name'] = $name;
$child['user']['avatar'] = $avatar;
$activityArray[$groupNumber]['child'][] = $child;
}
}
$previousGroupNumber = $groupNumber;
}
return $activityArray;
}
First we refactor $data.
$data = $activityItem->getData();
$data is an array stored in the database like this:
Joined Data:
a:2:{s:6:"userId";s:1:"1";s:8:"username";s:8:"mbwagner";}
Watchlisted Data
a:5:{s:13:"documentaryId";i:728;s:16:"documentaryTitle";s:24:"The U.S. vs. John Lennon";s:18:"documentaryExcerpt";s:160:"In retrospect, it seems absurd that the United States government felt so threatened by the presence of John Lennon that they tried to have him deported. But tha";s:20:"documentaryThumbnail";s:24:"cover/usvsjohnlennon.jpg";s:15:"documentarySlug";s:22:"the-u-s-vs-john-lennon";}
The problem with this way is the data is hardcoded into the database, what happens if the data changes? So I decide to populate data on the fly.
Replace:
$data = $activityItem->getData();
with:
$dataStrategyContext = new DataStrategyContext(
$type,
$this->request,
$this->documentaryService,
$this->commentService);
$data = $dataStrategyContext->createData($activityItem);
Here we use the Strategy Pattern to populate data.
class DataStrategyContext
{
/**
* @var StrategyInterface|null
*/
private $strategy = null;
public function __construct(
string $type,
Request $request,
DocumentaryService $documentaryService,
CommentService $commentService)
{
switch ($type) {
case ActivityType::WATCHLIST:
$this->strategy = new StrategyWatchlist(
$request,
$documentaryService
);
break;
case ActivityType::COMMENT:
$this->strategy = new StrategyComment(
$request,
$commentService
);
break;
case ActivityType::JOINED:
$this->strategy = new StrategyJoined();
break;
case ActivityType::ADDED:
$this->strategy = new StrategyAdded(
$documentaryService
);
break;
}
}
/**
* @param Activity $activityEntity
* @return mixed
*/
public function createData(Activity $activityEntity)
{
return $this->strategy->createData($activityEntity);
}
}
StrategyWatchlist would look like the following:
class StrategyWatchlist implements StrategyInterface
{
/**
* @var Request
*/
private $request;
/**
* @var DocumentaryService
*/
private $documentaryService;
/**
* @param Request $request
* @param DocumentaryService $documentaryService
*/
public function __construct(
Request $request,
DocumentaryService $documentaryService)
{
$this->request = $request;
$this->documentaryService = $documentaryService;
}
/**
* @param Activity $activityEntity
* @return Data
*/
public function createData(Activity $activityEntity)
{
$documentaryId = $activityEntity->getObjectId();
$documentary = $this->documentaryService->getDocumentaryById($documentaryId);
$watchlistData = new WatchlistData();
$watchlistData->setDocumentaryId($documentary->getId());
$watchlistData->setDocumentaryTitle($documentary->getTitle());
$watchlistData->setDocumentarySlug($documentary->getSlug());
$watchlistData->setDocumentarySummary($documentary->getSummary());
$poster = $this->request->getScheme() .'://' . $this->request->getHttpHost() . $this->request->getBasePath() . '/uploads/posters/' . $documentary->getPoster();
$watchlistData->setDocumentaryPoster($poster);
return $watchlistData;
}
}
The next thing is to refactor parent and children from this:
if ($type == ActivityType::Like) {
if ($groupNumber != $previousGroupNumber) {
$data['documentaryThumbnail'] = $this->request->getScheme() .'://' . $this->request->getHttpHost() . $this->request->getBasePath() . '/uploads/posters/' . $data['documentaryThumbnail'];
$activityArray[$groupNumber]['parent']['data'] = $data;
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
} else {
$data['documentaryThumbnail'] = $this->request->getScheme() .'://' . $this->request->getHttpHost() . $this->request->getBasePath() . '/uploads/posters/' . $data['documentaryThumbnail'];
$child['data'] = $data;
$child['user']['name'] = $name;
$child['user']['avatar'] = $avatar;
$activityArray[$groupNumber]['child'][] = $child;
}
} else if ($type == ActivityType::Comment) {
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
$activityArray[$groupNumber]['parent']['data'] = $data;
} else if ($type == ActivityType::Joined) {
if ($groupNumber != $previousGroupNumber) {
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
} else {
$child['user']['name'] = $name;
$child['user']['avatar'] = $avatar;#
$activityArray[$groupNumber]['child'][] = $child;
}
} else if ($type == ActivityType::Added) {
if ($groupNumber != $previousGroupNumber) {
$activityArray[$groupNumber]['parent']['data'] = $data;
$activityArray[$groupNumber]['parent']['user']['name'] = $name;
$activityArray[$groupNumber]['parent']['user']['avatar'] = $avatar;
} else {
$child['data'] = $data;
$child['user']['name'] = $name;
$child['user']['avatar'] = $avatar;
$activityArray[$groupNumber]['child'][] = $child;
}
}
$previousGroupNumber = $groupNumber;
}
After refactoring and creating ActivityParent and ActivityChild it should look like this:
if ($type == ActivityType::Like) {
if ($groupNumber != $previousGroupNumber) {
$activityParent = new ActivityParent();
$activityParent->setName($name);
$activityParent->setUsername($username);
$activityParent->setAvatar($avatar);
$activityParent->setData($data);
$activityArray[$groupNumber]['parent'] = $activityParent->toArray();
} else {
$activityChild = new ActivityChild();
$activityChild->setData($data);
$activityChild->setUsername($username);
$activityChild->setName($name);
$activityChild->setAvatar($avatar);
$activityArray[$groupNumber]['child'][] = $activityChild->toArray();
}
} else if ($type == ActivityType::Comment) {
$activityParent = new ActivityParent();
$activityParent->setName($name);
$activityParent->setUsername($username);
$activityParent->setAvatar($avatar);
$activityParent->setData($data);
} else if ($type == ActivityType::Joined) {
if ($groupNumber != $previousGroupNumber) {
$activityParent = new ActivityParent();
$activityParent->setName($name);
$activityParent->setUsername($username);
$activityParent->setAvatar($avatar);
$activityParent->setData($data);
} else {
$activityChild = new ActivityChild();
$activityChild->setData($data);
$activityChild->setUsername($username);
$activityChild->setName($name);
$activityChild->setAvatar($avatar);
$activityArray[$groupNumber]['child'][] = $activityChild->toArray();
}
} else if ($type == ActivityType::Added) {
if ($groupNumber != $previousGroupNumber) {
$activityParent = new ActivityParent();
$activityParent->setName($name);
$activityParent->setUsername($username);
$activityParent->setAvatar($avatar);
$activityParent->setData($data);
} else {
$activityChild = new ActivityChild();
$activityChild->setData($data);
$activityChild->setUsername($username);
$activityChild->setName($name);
$activityChild->setAvatar($avatar);
$activityArray[$groupNumber]['child'][] = $activityChild->toArray();
}
}
$previousGroupNumber = $groupNumber;
}
Notice there are duplicate times when we create ActivityParent and ActivityChild. Only certain types have children:
class ActivityType
{
const LIKE = "like";
const COMMENT = "comment";
const FOLLOW = "follow";
const JOINED = "joined";
const ADDED = "added";
/**
* @return array
*/
public static function getAllTypes()
{
return [
self::LIKE,
self::ADDED,
self::JOINED,
self::COMMENT,
self::FOLLOW
];
}
/**
* @return array
*/
public static function getTypesWithChildren()
{
return [
self::LIKE,
self::JOINED,
self::ADDED
];
}
/**
* @param string $type
* @return bool
*/
public static function hasChildren(string $type)
{
return in_array($type, self::getTypesWithChildren());
}
}
Then we update the convertActivityToArray() function to this:
$hasChildren = ActivityType::hasChildren($type);
if ($hasChildren) {
if ($groupNumber != $previousGroupNumber) {
$activityParent = new ActivityParent();
$activityParent->setName($name);
$activityParent->setUsername($username);
$activityParent->setAvatar($avatar);
$activityParent->setData($data);
$activityArray[$groupNumber]['parent'] = $activityParent->toArray();
} else {
$activityChild = new ActivityChild();
$activityChild->setData($data);
$activityChild->setUsername($username);
$activityChild->setName($name);
$activityChild->setAvatar($avatar);
$activityArray[$groupNumber]['child'][] = $activityChild->toArray();
}
} else {
$activityParent = new ActivityParent();
$activityParent->setName($name);
$activityParent->setUsername($username);
$activityParent->setAvatar($avatar);
$activityParent->setData($data);
$activityArray[$groupNumber]['parent'] = $activityParent->toArray();
}
Notice ActivityParent and ActivityChild are doing the same thing so I refactored it to just ActivityObject. Plus I moved it above the IF statements.
$activityObject = new ActivityObject();
$activityObject->setName($name);
$activityObject->setUsername($username);
$activityObject->setAvatar($avatar);
$activityObject->setData($data);
$hasChildren = ActivityType::hasChildren($type);
if ($hasChildren) {
if ($groupNumber != $previousGroupNumber) {
$activityArray[$groupNumber]['parent'] = $activityObject->toArray();
} else {
$activityArray[$groupNumber]['child'][] = $activityObject->toArray();
}
} else {
$activityArray[$groupNumber]['parent'] = $activityObject->toArray();
}
Next we add ActivityItemOject to hold the array keys and values.
class ActivityItemObject
{
/**
* @var string
*/
private $type;
/**
* @var \DateTime
*/
private $created;
/**
* @var ActivityObject
*/
private $parent;
/**
* @var ActivityObject[]
*/
private $children;
public function __construct()
{
$this->children = [];
}
.....
/**
* @return array
*/
public function toArray()
{
$children = [];
foreach ($this->children as $child) {
$children[] = $child->toArray();
}
$array = [
'type' => $this->type,
'created' => $this->created,
'parent' => $this->parent->toArray(),
'child' => $children
];
return $array;
}
And finally the completed code:
/**
* @param ArrayCollection|Activity[] $activity
* @return array
*/
private function convertActivityToArray(array $activity)
{
$activityMap = [];
$previousGroupNumber = 0;
/** @var Activity $activityEntity */
foreach ($activity as $activityItem) {
$groupNumber = $activityItem->getGroupNumber();
if (array_key_exists($groupNumber, $activityMap) != null) {
$activityItemObject = $activityMap[$groupNumber];
} else {
$activityItemObject = new ActivityItemObject();
}
$type = $activityItem->getType();
$created = $activityItem->getCreatedAt();
$activityItemObject->setType($type);
$activityItemObject->setCreated($created);
$dataStrategyContext = new DataStrategyContext(
$type,
$this->request,
$this->documentaryService,
$this->commentService);
$data = $dataStrategyContext->createData($activityItem);
$user = $activityItem->getUser();
$name = $user->getName();
$avatar = $this->request->getScheme() .'://' . $this->request->getHttpHost() . $this->request->getBasePath() . '/uploads/avatar/' . $user->getAvatar();
$username = $user->getUsername();
$activityObject = new ActivityObject();
$activityObject->setName($name);
$activityObject->setUsername($username);
$activityObject->setAvatar($avatar);
$activityObject->setData($data);
$hasChildren = ActivityType::hasChildren($type);
if ($hasChildren) {
if ($groupNumber != $previousGroupNumber) {
$activityItemObject->setParent($activityObject);
} else {
$activityItemObject->addChild($activityObject);
}
} else {
$activityItemObject->setParent($activityObject);
}
$activityMap[$groupNumber] = $activityItemObject;
$previousGroupNumber = $groupNumber;
}
$display = [];
/** @var ActivityItemObject $value */
foreach ($activityMap as $key => $value) {
$display[$key] = $value->toArray();
}
return $display;
}