Convert to Array or Collection or List - php

I have a big chunk of data in this format:
[
{"date":"2018-11-17"},{"weather":"sunny"},{"Temp":"9"},
{"date":"2014-12-19"},{"Temp":"10"},{"weather":"rainy"},
{"date":"2018-04-10"},{"weather":"cloudy"},{"Temp":"15"},
{"date":"2017-01-28"},{"weather":"sunny"},{"Temp":"12"}
]
Is there any faster and more efficient way to organize and save it the database for future reference? Like making a comparison of the temperature for different days etc. [date,weather,Temp] are supposed to be in one set.
I've tried str_replace() but I'd like to know if there's any better way.

Taking into account your comment, it seems that is an array of objects in which every three objects make a record (that is form of: date, weather & temp) so you can create this setup with the help of collections:
$string = ['your', 'json', 'string'];
$records = collect(json_decode($string))
->chunk(3) // this creates a subset of every three items
->mapSpread(function ($date, $weather, $temp) { // this will map them
return array_merge((array) $date, (array) $weather, (array) $temp);
});
This will give you this output:
dd($records);
=> Illuminate\Support\Collection {#3465
all: [
[
"date" => "2018-11-17",
"weather" => "sunny",
"Temp" => "9",
],
[
"date" => "2014-12-19",
"Temp" => "10",
"weather" => "rainy",
],
[
"date" => "2018-04-10",
"weather" => "cloudy",
"Temp" => "15",
],
[
"date" => "2017-01-28",
"weather" => "sunny",
"Temp" => "12",
],
],
}
PS: To get the array version of this collections just attach ->all() at the end.
You can check in the Collections documentation a good explanation of the chunk() and mapSpread() methods as well of the rest of the available methods.

Related

How to get a object value nested inside document in a collection in MongoDB PHP

I have stored a document set in MongoDB like below where ID is generated automatically through PHP
{
"_id": {
"$oid": "5ff745237d1b0000860007a6"
},
"user": "5ff741fb7d1b0000860007a4",
"name": "Test",
"slug": "fed73b70d080584c21e0a4353825ef91",
"category": "5fef4a467d1b000086000745",
"images": [{
"100x128": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg",
"200x256": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg",
"300x385": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg",
"500x642": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg"
},
{
"100x128": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg",
"200x256": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg",
"300x385": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg",
"500x642": "test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg"
}],
}
Now the problem is I want to get value from images object from key 100x128 only from the first array.
I am using the below code to achieve
$productsArray = $collection->aggregate(
[
[
'$match' => [
'user_id' => $user_id
]
],
[ '$unwind' => '$images' ],
[ '$unwind' => '$images.100x128' ],
[
'$project' => [
'name' => 1,
'image' => '$images'
]
]
]
);
and I always get an output as below
MongoDB\Model\BSONDocument Object
(
[storage:ArrayObject:private] => Array
(
[_id] => MongoDB\BSON\ObjectId Object
(
[oid] => 5ff745357d1b0000860007a7
)
[name] => Test 1
[image] => MongoDB\Model\BSONDocument Object
(
[storage:ArrayObject:private] => Array
(
[100x128] => test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg
[200x256] => test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg
[300x385] => test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg
[500x642] => test/year/month/image_da4181762396a31ff44f5ddc08ce6186.jpeg
)
)
)
)
I am not sure how to get just that specific value. Where am i going wrong here?
As far as i am getting your question it looks like you would need to understand the mongo concept. So you might get an array representation in UI or in PHP but at the end of day it is object.
Look closely the format that you have posted there is no array but multiple {} in that object just like JS so forget about array concept here.
If my understanding is correct this is what you are looking for
$productsArray = $collection->aggregate(
[
[
'$match' => [
'user_id' => $user_id
]
],
[ '$unwind' => '$images' ],
[ '$unwind' => '$images.100x128' ],
[
'$project' => [
'name' => 1,
'image' => '$images.100x128'
]
]
]
);
Whatever you have googled or used it is correct just you need to add the second unwind value i.e $images.100x128 instead of $images in the $project variable.
You can understand this way that $unwind variable uses recursive logic to get output as you want. Like let's say you have an array inside an array first it will get values from internal array then it will come to parent array and then so on. So the last value in $unwind variable should be used.
I am not sure if it will work on multiple objects but as you said you just need the first one. This should do the charm.

json_encode treats a single element array as object [duplicate]

This question already has answers here:
How to add square braces around subarray data inside of a json encoded string?
(3 answers)
Closed 6 months ago.
this is the json i have to produce
{
"email": "example#example.com",
"campaign": {
"campaignId": "p86zQ"
},
"customFieldValues": [
{
"customFieldId": "y8jnp",
"value": ["18-29"]
}
]
}
if i use
$data = [
"email" => $_POST['mail'],
"campaign" => [
"campaignId" => "4JIXJ"
],
"customFieldValues" => [
"customFieldId" => "y8jnp",
"value" => ["18-29"]
]
];
and i do json_encode($data)
value is an object, but it should be an array with a single element. Somehow json_encode treats it as an object. Can i force it to treat it as an array with a single element ?
Thanks in Advance
Adrian
At the moment, you have a single array with 2 elements, instead of an array with a single element of a sub-array. In order to get the json in the first section, you need to add another array level.
$data = [
"email" => $_POST['mail'],
"campaign" => [
"campaignId" => "4JIXJ"
],
"customFieldValues" => [
[
"customFieldId" => "y8jnp",
"value" => ["18-29"]
]
]
];
That will give you this:
{
"email": null,
"campaign": {
"campaignId": "4JIXJ"
},
"customFieldValues": [
{
"customFieldId": "y8jnp",
"value": ["18-29"]
}
]
}
if there is one single element in the array the json_encode will treat is as an object and the key of the object is the index of the array
to treat it as an array you can use array_values(<your_array>)

get child from multidimensional array based on value php

i'm working on a project where i have a structured object like this:
$main_array= [
[
"key"=> "home",
"value":=> "Go Home!"
],
[
"key"=> "business",
"value"=> "Go to Work!"
],
[
"key"=> "other",
"value"=> "Go where you want!"
]
]
i'd like to know if there is a way to retrieve the object based on the "key" parameter.
What i want to do is "extract" the nested array like
$home_array=["key"=> "home","value":=> "Go Home!"]
and so on for "business" and "others".
in javascript, i can use jquery or underscore to get what i want, is there a php method to achieve this, or something to simulate a "where" clause in a multidimensional array/object?
thak you in advance
You can easily convert the array you have to have the 'key' column to be the main index using array_column()...
$main_array= [
[
"key"=> "home",
"value"=> "Go Home!"
],
[
"key"=> "business",
"value"=> "Go to Work!"
],
[
"key"=> "other",
"value"=> "Go where you want!"
]
];
$out = array_column($main_array, null, "key");
print_r($out['business']);
Outputs...
Array
(
[key] => business
[value] => Go to Work!
)
A few possibilities to get a single item matching a specific key:
Iterate the main array and stop when you get to a child that has the proper key:
$object = (function($key) use($main_array) {
foreach ($main_array as $object) {
if ($object['key'] == $key) return $object;
}
})('business');
(This example uses an anonymous function, but you can just use a simple foreach loop and break when you find the key.)
Reindex the main array and look up the child by key:
$indexed = array_column($main_array, null, 'key');
$object = $indexed['business'];
Construct the array using the key as the index to begin with. Using string keys doesn't preclude the array elements being other arrays that can contain multiple values.
$main_array= [
'home' => ["value"=> "Go Home!"],
'business' => ["value"=> "Go to Work!"],
'other' => ["value"=> "Go where you want!"]
];
Methods 2 and 3 require that they key is unique. Method 1 doesn't, but it will just return the first instance it finds.
If you do have multiple instances of a key, you probably want array_filter. This will work more like the "where clause" you referred to.
$key = 'home';
$filtered = array_filter($main_array, function($item) use ($key) {
return $item['key'] == $key;
});
This will return multiple items instead of just one.

Unset object variable's property in a method, affected its value in parent scope?

I made a small library to read database-exported xml file, and output it as an structured associate array. It can also filter the output by table columns.
After I've done the code, when I play with this class I find something weird. unset is acting as if the variable was passed by reference.
I could not spot any possible bugs, so I put up my the particular method where $this->rows is being set, hoping someone could enlight me.
private function populateData($rowLimit)
{
if (!$this->getColumns()) {
throw new ReaderException(__FUNCTION__ . " # Unable to get columns name.");
}
// database->tableName->rows
$rows = $this->getSimpleXmlElement()->children()->children();
if ($this->getFilteredColumns()) {
$toRemoves = array_values(array_diff($this->getColumns(),
$this->getFilteredColumns()));
foreach ($rows as $row) {
foreach ($toRemoves as $toRemove) {
unset($row->{$toRemove});
}
}
}
$rows = $this->simpleXmlToArray($rows)['row'];
if ($rowLimit) {
$limited = [];
for ($i = 0; $i < $rowLimit; $i++) {
$limited[] = $rows[$i];
}
$this->setRows($limited);
} else {
$this->setRows($rows);
}
$structArray = [
'database' => $this->getDatabaseName(),
'table' => $this->getTableName(),
'columns' => !$this->getFilteredColumns() ? $this->getColumns()
: $this->getFilteredColumns(),
'rows' => $this->getRows()
];
$this->setStruct($structArray);
return $this;
}
$xml = new SomeXmlReader($companyTableFilePath);
$xml->get(1);
output:
[
"database" => "myDatabase",
"table" => "companies",
"columns" => [
"id",
"name",
"license_no",
"phone",
"created",
"modified",
],
"rows" => [
[
"id" => "1",
"name" => "SOME COMPANY NAME",
"license_no" => "884652",
"phone" => null,
"created" => "2015-09-25 16:01:57",
"modified" => "2015-09-25 16:01:57",
],
],
]
When I tried to filter off some columns, I do
$xml->setFilteredColumns(['id','name'])->get(1);
it returns the result as expected: the columns have been trimmed.
Output:
[
"database" => "myDatabase",
"table" => "companies",
"columns" => [
"id",
"name",
],
"rows" => [
[
"id" => "1",
"name" => "SOME COMPANY NAME",
],
],
]
However, right after this line, when I test it again with
$xml->setFilteredColumns(['id','name','phone'])->get(1);
on the next line, something went wrong. The output is:
[
"database" => "myDatabase",
"table" => "companies",
"columns" => [
"id",
"name",
"phone",
],
"rows" => [
[
"id" => "1",
"name" => "SOME COMPANY NAME",
],
],
]
After some tracing, I find that $this->getSimpleXmlElement() had been modified by unset. Within this app lifecycle, I can no longer get the complete/original value of $this->getSimpleXmlElement().
Is there a fundamental mistake I've made, or this is a PHP language behaviour?
There is rather a lot of code to follow through here - consider making a more compact example - but it sounds like your misunderstanding is fairly simple.
Objects are never copied when going from one scope to another, because a variable doesn't "contain" an object, it "points at" one (this is not quite the same as pass-by-reference, but the effect is similar in many cases).
So when you run unset($row->{$toRemove});, you are indeed editing the $row object, and by extension the whole SimpleXML document.
If you want a copy of an object to work with, you have to explicitly "clone" it, as discussed in the PHP manual.

Simple PHP Templating With Looping

Hey I wrote a very simple template system for our designers to use. Its uses str_replace under the hood to do its thing.
Its works great! The issue now is i'd like do some looping (foreach) on some data passed.
Here's and example of the template code
$var_c = [
[
"head" => "a",
"body" => "b",
"foot" => "c"
],
[
"head" => "x",
"body" => "y",
"foot" => "z"
]
];
$tpl_vars = [
"__{var_a}__",
"__{var_b}__",
"__{var_c}__"
];
$real_vars = [
$var_a,
$var_b,
$var_c
];
str_replace($tpl_vars, $real_vars, $content_body);
Note $var_c contains an array and i'd like to loop through this array. How do i achieve that.
For structure i was thinking
__startloop__
loop var_c as c
c[head]
c[body]
c[foot]
__endloop__
I cant seem to get my head around how to code this. :)
UPDATE: Twig, smarty and the likes are too big and cumbersome for the work. Its also introduces the learning curve for the designers to adopt a templating language.
See my Text-Template class. It supports conditions (if), loops (for) and filters: https://github.com/dermatthes/text-template
Example template (String in a variable):
Hello {= name},
Your list of Items:
{for curItem in items}
{=#index1}: {= curItem.name}
{/for}
PHP-Code:
<?php
$data = [
"name" => "Some Username",
"items" => [
["name" => "First Item"],
["name" => "Second Item"]
]
];
$tt = new TextTemplate ($templateString);
echo $tt->apply ($data);
This should do the job.

Categories