I am getting article/articles from an API. The JSON object varies, some articles have properties that some other do not have.
I need to iterate through the items and manipulate the properties, if they are set.
What is the best way to tackle this?
Actually for now I do something that I find very ugly...
foreach ($items as $key => $item) {
if(isset($item->title)){
$parsed[$key]['title'] = $this->formatTitle($item->title);
}
if(isset($item->salutation)){$parsed[$key]['salutation'] = $item->salutation;}
if(isset($item->eventDate) && isset($item->enventEndDate)){
$parsed[$key]['eventDates'] = $this->ersDate($item->eventDate, $item->eventEndDate);
$parsed[$key]['startDateTimestamp'] = $this->toTimestamp($item->eventDate);
} elseif(isset($item->eventDate) && !isset($item->enventEndDate)){
$parsed[$key]['eventDates'] = $this->ersDate($item->eventDate);
$parsed[$key]['startDateTimestamp'] = $this->toTimestamp($item->eventDate);
}
//... code continues ...
Since your source has unpredictable shape, I don't think there is any way around parsing the data.
You can abstract the ugliness in a separate function, so that your main script just does:
$parsed = parseAPI($items);
If you use $items = json_decode($apiResponse,true), you get an array instead of an object. You can then use the + operators on arrays along with a default array to cast all API responses to the same shape.
$defaultItem = [
'salutation' => null,
'eventDate' => null,
'eventEndDate' => null,
...
];
Now when you get items from the API, you can do:
$items = json_decode($apiResponse,true);
foreach($items as &$item) $item += $defaultItem;
Now each member of $items has all the keys you expect. If any key was missing, $defaultItem's matching key and value was inserted.
Related
I have a Laravel site I am modifying, but there are some parts of the PHP code I don't quite understand, which are "array objects" or "object arrays". You see, I don't even know what to call them and so can't find a tutorial or basic data on it. Below is the code that I am dealing with:
private function parseMetric($result, $view)
{
$data = collect([]);
$result->each(function($item) use ($data, $view) {
if (isset($item->metric->{$view})) {
$data->push((object)[
'label' => $item->metric->{$view},
'value' => $item->metric->count
]);
}
});
...
From what I can tell, this creates an object out of $result. If I json_encode this and echo it out I get this:
[{"label":"1k-25k","value":14229},
{"label":"1mm+","value":1281},
{"label":"25k-50k","value":398},
{"label":"50k-75k","value":493},
{"label":"75k-100k","value":3848},
{"label":"100k-150k","value":9921},
{"label":"150k-200k","value":4949},
{"label":"200k-250k","value":3883},
{"label":"250k-300k","value":2685},
{"label":"300k-350k","value":2744},
{"label":"350k-500k","value":4526},
{"label":"500k-1mm","value":8690}]
Now this is obviously an array of arrays... or is it? Is it an array of objects? Or is it an object containing arrays? But the most important question is, how do I access and move or change the individual objects/arrays in this object? For example, I want to take the second object/array, which is:
{"label":"1mm+","value":1281}
and move it to the end. How do I do that? How do I find it? I used the following piece of code to find it which is pretty clunky:
$pos = strpos(json_encode($result), '1mm+');
if($pos){
Log::debug('Enrich 73, I found it!!!!!!!!!!!!!!!!!!!!!!!!!!!');
}
And once I find it, how do I move that array/object to the end of the whole object?
And finally, where can I find some kind of tutorial, or documentation, that describes this construct and how to work with it?
There is no need to json_encode the data. Since the data is an instance of Laravel Collection, you can manipulate it like so
$item = $data->firstWhere('label', '1mm+'); // get the item
$data = $data->filter(fn($value, $key) => $value->label !== '1mm+') // remove $item from $data
->push($item); // move $item to the end of data
Acording to Laravel documnentation for Collections, you can try something like this :
To find index of element with name = "1mm+" :
$index = $datas->search(function ($item, $key) {
return $item['name'] == "1mm+";
});
to get an element at a given index :
$element = $datas->get($index);
to Move element at index 3 to the end :
$index = 3
$elementToMove = $data->splice($index, 1);
$datas->push($elementToMove);
Here is a link to the document used : https://laravel.com/docs/8.x/collections
So I have a var_dump($instagram->get_images()); that gives me the following output:
I want to use array_map to map through all the properties and use them inside a foreach loop later on.. but I'm running into some issues:
Here is the attempt that I have:
$mediaUrls = array_map(function($entry) {
return [
'media_url' => $entry['media_url'],
];
}, $instagram->get_images());
I'm getting back the following error:
Could someone assist me on properly array_mapping through the objects and then later be able to use foreach ($MediaUrls as $media) etc...
The error is correct. You're using array map on an object. But the object does have a ->data property that is an array. But the items in the array are objects, so you'll need to refer to their properties rather than using array syntax.
$images = $instagram->get_images();
$mediaUrls = array_map(function($entry) {
return [
'media_url' => $entry->media_url,
];
}, $images->data);
Couple of suggestions. You said, "I want to use array_map to map through all the properties and use them inside a foreach loop later on."
You can reiterate $images->data later on, so I don't really see the value of making another array just for that purpose
foreach ($images->data as $imageData) {
// do something with $imageData->media_url
}
This would be almost exactly the same as iterating the array you're making with array_map.
foreach ($images->data as $imageData) {
// do something with $imageData['media_url']
}
If you want to get an array of just the urls, you can do it more simply with array_column.
$images = $instagram->get_images();
$mediaUrls = array_column($images->data, 'media_url');
(This won't give you the same result. It will be an array of strings rather than an array of arrays.)
I have an array of objects which have been posted from a Vue Axios function, which I wish to loop over and save into a database. They are answers to a question.
I have passed in $data which is the array of answer objects (each has a content, correct and mark property), and the $id of the question they belong to. When I return $data, it shows me the array of objects with all the correct properties. When I return $data[0], I can access the first object. But when I try and foreach as below, it complains that $content doesn't exist. Running count() on $data also errors. What is wrong here?
Route::post('answers/{id}', function (Request $data, $id) {
foreach ($data as $value) {
$post[] = [
'user_id' => 1,
'question_id' => $id,
'content' => $value->content,
'correct' => $value->correct,
'mark' => $value->mark
]);
}
Answer::save($post);
});
You are trying to iterate over the hole $request object, which is an instance of the Request class. To access the received values first get them:
// To get all the data
$data = $request->all();
// or..
// To get just a specific value
$data = $request->get('key');
// or..
// only a list of allowed elements
$data = $request->only('here', 'goes', 'your', 'keys');
So, in case your frontend are sending an array of items under the key items. Just get them like mentioned above:
$items = $request->get('items');
Then you can use the foreach():
$items = $request->get('items');
foreach($items as $item)
{
// your operations
}
You can read more about Retrieving Input, in the documentation.
Trying to figure out how to parse a collection and put multiple items into the same key in another collection.
Currently I'm doing this using an array and then I make a collection out of it, but the items inside are not of type Collection, each key is an array and I can't use methods like first() on those arrays. Yes, I can use [0] instead, but I'd prefer to have access to methods available for collections.
$some_array = [];
// Parsing the existing collection using foreach
foreach ($items_collection as $item) {
// Doing some checks
if ($item->some_attribute1 == 1
&& #$item->some_relation->some_attribute2
) {
// Putting the item into the array with a specific dynamic key
$some_array[$item->some_relation->some_attribute2][] = $item->some_relation;
}
else if ($item->some_attribute1 == 0
&& #$item->some_relation->some_attribute3) {
// Putting the item into the array with a specific dynamic key
$some_array[$item->some_relation->some_attribute3][] = $item->some_relation;
}
}
// Defining a new Collection
$new_collection = new Collection();
// Parsing the array of groups of items and putting them in the newly created Collection by their key
foreach ($some_array as $key => $key_items) {
$new_collection->put($key, $key_items);
}
If to make something like this
$some_collection = new Collection();
foreach ($items_collection as $item) {
if ($item->some_attribute1 == 1
&& #$item->some_relation->some_attribute2
) {
$some_collection->put($item->some_relation->some_attribute2, $item->some_relation);
}
else if ($item->some_attribute1 == 0
&& #$item->some_relation->some_attribute3) {
$some_collection->put($item->some_relation->some_attribute3, $item->some_relation);
}
}
then instead of storing all the items in the same key the new items will just override the old ones. Is there a way to put multiple items in the same key using put()?
Thank you in advance!
Seems that the issue was that I wasn't converting the $key_items into a collection in the last foreach.
Now I just used the collect() method on $key_items to make it into a Collection and everything works now.
foreach ($some_array as $key => $key_items) {
$new_collection->put($key, collect($key_items));
}
I hope someone will find this workaround useful until a more elegant solution will be found.
Taking a dictionary as an example, let's say I've got the following array to catalog the pronunciations of a given word:
$pronunciation["text"] = array("alpha", "beta", "gamma", "delta", "epsilon");
$pronunciation["definition"] = array(1, 2, NULL, NULL, 1);
text contains the pronunciation that will be displayed to the user, while definition contains the ID for the definition in the list where that pronunciation applies. If definition is NULL, it's not associated with any particular definition, and should be displayed elsewhere in the entry.
Now, if I try to do something like the following:
$key = array_search(1, $pronunciation["definition"]);
All I'll get is 0, since that's the first key in definition that contains the value. Likewise, substituting NULL returns 3, however both of these actually have another related key that I'm trying to fetch.
Is there a way to get it to return all related keys without having to resort to brute force methods such as a for loop?
Try this one:
array_keys($pronunciation["definition"],1)
I'm afraid there is not a function that do that as you want. Edit: Nope, there is! See Jauzsika answer.You have to use a foreach or a for loop.
function array_search_all($needle, $haystack) {
$keys = array();
foreach ($haystack as $k => $v) if ($v === $needle) $keys[] = $k;
return $keys;
}
Call:
$keys = array_search_all(1, $pronunciation["definition"]);
I don't know wheter it's faster or not, but I think if there're a set of data which you have to search in, it's better to set up an array, where the data is the key.
$data["alpha"] = 1
$data["beta"] = 1
//...
All your foreach should be modified from
foreach ($data as $item) {
to
foreach ($data as $item => $value) {
Restructure your arrays into a single array keyed on the text and using the definition as the value:
$pronunciation = array("alpha" => 1,
"beta" => 2,
"gamma" => NULL,
"delta" => NULL,
"epsilon" => 1);
Then use array_filter() to build a subset of the values you need
$searchVal = 1;
$subset = array_filter($pronunciation,
create_function('$value', 'return $value == '.$searchVal.';')
);
var_dump($subset);