I am trying to add an object ("new_item") to an array if objects in a master JSON object ("master"). If the new object ID already exists in the array I first remove that object from the array before then adding it again afterwards (so there is only one entry per ID).
For some reason when I do this the Array of objects gets converted to a list of objects which I don't want.
This doesn't happen if I only remove the duplicate or allow duplicates, it only seems to happen when I remove one element and then add another.
$object = '{"master":[{"id":"1","author":"A"},{"id":"2","author":"B"}]}';
$object = json_decode($object);
$new_item = '{"id":"2","author":"B"}';
$new_item = json_decode($new_item);
foreach ( $object->master as $key => $item ) {
if ( $item->id == $new_item->id ) {
unset($object->master[$key]);
}
}
if ( count($object->master) == 0 ) {
$object->master = Array($new_item);
} else {
array_push($object->master, $new_item);
}
echo json_encode($object);
Outputs
{"master":{"0":{"id":"1","author":"A"},"2":{"id":"2","author":"B"}}}
As oppose to
{"master":[{"id":"1","author":"A"},{"id":"2","author":"B"}]}
json_encode() only encodes an array as a JSON array if the indexes are sequential starting from 0. Otherwise, it's encoded as an object. Since you do unset($object->master[$key]), you create a gap in the indexes, so you get an object, as there's no other way to indicate that the array doesn't have anything at index 1.
You can use array_values() to renumber the indexes, to get rid of the gap.
There's no need to check if the count is 0 before pushing the new item, since you can push onto an empty array.
array_push($object->master, $new_item);
$object->master = array_values($object->master);
You say that the problem doesn't happen if you only remove the duplicate without adding another. It should still happen, unless the duplicate happens to be the last element. Then there's no gap in the index sequence, so it meets the criteria to be encoded as an array.
If IDs are unique, another solution would be to simply replace the duplicate with the new item, instead of unsetting it and later pushing the new item.
$found = false;
foreach ( $object->master as $key => $item ) {
if ( $item->id == $new_item->id ) {
$object->master[$key] == $new_item;
$found = true;
break;
}
}
if (!$found) {
$object->master[] = $new_item;
}
U can use this:
$object = array_values($object);
Related
I am getting duplicate data in an array and that data is storing in my DB. I am using array_unique to refine the duplicate data but it is not working. Please tell me is there any other way to make data unique and store in DB this way.
if (preg_match($keywords, $links[$i]->href)) {
if (filter_var($links[$i]->href, FILTER_VALIDATE_URL) !== false) {
array_push($mainNews, $links[$i]->href);
}
}
return (array_unique($mainNews));
Error I am getting:
Undefined array key 1 at C:\xampp\htdocs\pacra-crawlers\modules\crawlers\services\MainNewsRepository.php:46
for ($i = 0; $i < count($mainNewsLinks); $i++) {
$mainNews = new MainNews();
$mainNews->newspaper_id = $this->newspaperId;
$mainNews->sector_id = $sectorId;
$mainNews->url = $mainNewsLinks[$i];
$mainNews->save();
}
return ['status' => true];
}
C:\xampp\htdocs\pacra-crawlers\modules\crawlers\services\MainNewsRepository.php:46
Illuminate\Foundation\Bootstrap\HandleExceptions::handleError("Undefined array key 1", "C:\xampp\htdocs\pacra-crawlers\modules\crawlers\services\MainNewsRepo
sitory.php")
array_unique is working however although it is removing duplicates it is maintaining the same keys i.e.
If you had the following items in an array with
position/key value
0 a
1 a
2 b
array_unique would return
position/key value
0 a
2 b
which is why you are getting the Undefined array key when looping through the array based on the incrementing index $i.
Based on your sample you could use a foreach loop since you are only interested in the value eg
foreach($mainNewsLinks as $mainNewsLink) {
$mainNews = new MainNews();
$mainNews->newspaper_id = $this->newspaperId;
$mainNews->sector_id = $sectorId;
$mainNews->url = $mainNewsLink;
$mainNews->save();
}
If you would like to continue indexing or iterating through each element based on an index, you could use array_values in your return eg
return array_values(array_unique($mainNews));
from your function to reset the array keys to incrementing indexes
This question has been asked a thousand times, but each question I find talks about associative arrays where one can delete (unset) an item by using they key as an identifier. But how do you do this if you have a simple array, and no key-value pairs?
Input code
$bananas = array('big_banana', 'small_banana', 'ripe_banana', 'yellow_banana', 'green_banana', 'brown_banana', 'peeled_banana');
foreach ($bananas as $banana) {
// do stuff
// remove current item
}
In Perl I would work with for and indices instead, but I am not sure that's the (safest?) way to go - even though from what I hear PHP is less strict in these things.
Note that after foreach has run, I expected var_dump($bananas) to return an empty array (or null, but preferably an empty array).
1st method (delete by value comparison):
$bananas = array('big_banana', 'small_banana', 'ripe_banana', 'yellow_banana', 'green_banana', 'brown_banana', 'peeled_banana');
foreach ($bananas as $key=>$banana) {
if($banana=='big_banana')
unset($bananas[$key]);
}
2nd method (delete by key):
$bananas = array('big_banana', 'small_banana', 'ripe_banana', 'yellow_banana', 'green_banana', 'brown_banana', 'peeled_banana');
unset($bananas[0]); //removes the first value
unset($bananas[count($bananas)-1]); //removes the last value
//unset($bananas[n-1]); removes the nth value
Finally if you want to reset the keys after deletion process:
$bananas = array_map('array_values', $bananas);
If you want to empty the array completely:
unset($bananas);
$bananas= array();
it still has the indexes
foreach ($bananas as $key => $banana) {
// do stuff
unset($bananas[$key]);
}
for($i=0; $i<count($bananas); $i++)
{
//doStuff
unset($bananas[$i]);
}
This will delete every element after its use so you will eventually end up with an empty array.
If for some reason you need to reindex after deleting you can use array_values
How about a while loop with array_shift?
while (($item = array_shift($bananas)) !== null)
{
//
}
Your Note: Note that after foreach has run, I expected var_dump($bananas) to return an empty array (or null, but preferably
an empty array).
Simply use unset.
foreach ($bananas as $banana) {
// do stuff
// remove current item
unset($bananas[$key]);
}
print_r($bananas);
Result
Array
(
)
This question is old but I will post my idea using array_slice for new visitors.
while(!empty($bananas)) {
// ... do something with $bananas[0] like
echo $bananas[0].'<br>';
$bananas = array_slice($bananas, 1);
}
Question has been updated to clarify
For simple arrays, I find it convenient to use $arr[$key]++ to either populate a new element or increment an existing element. For example, counting the number of fruits, $arr['apple']++ will create the array element $arr('apple'=>1) the first time "apple" is encountered. Subsequent iterations will merely increment the value for "apple". There is no need to add code to check to see if the key "apple" already exists.
I am populating an array of arrays, and want to achieve a similar "one-liner" as in the example above in an element of the nested array.
$stats is the array. Each element in $stats is another array with 2 keys ("name" and "count")
I want to be able to push an array into $stats - if the key already exists, merely increment the "count" value. If it doesn't exist, create a new element array and set the count to 1. And doing this in one line, just like the example above for a simple array.
In code, this would look something like (but does not work):
$stats[$key] = array('name'=>$name,'count'=>++);
or
$stats[$key] = array('name'=>$name,++);
Looking for ideas on how to achieve this without the need to check if the element already exists.
Background:
I am cycling through an array of objects, looking at the "data" element in each one. Here is a snip from the array:
[1] => stdClass Object
(
[to] => stdClass Object
(
[data] => Array
(
[0] => stdClass Object
(
[name] => foobar
[id] => 1234
)
)
)
I would like to count the occurrences of "id" and correlate it to "name". ("id" and "name" are unique combinations - ex. name="foobar" will always have an id=1234)
i.e.
id name count
1234 foobar 55
6789 raboof 99
I'm using an array of arrays at the moment, $stats, to capture the information (I am def. open to other implementations. I looked into array_unique but my original data is deep inside arrays & objects).
The first time I encounter "id" (ex. 1234), I'll create a new array in $stats, and set the count to 1. For subsequent hits (ex: id=1234), I just want to increment count.
For one dimensional arrays, $arr[$obj->id]++ works fine, but I can't figure out how to push/increment for array of arrays. How can I push/increment in one line for multi-dimensional arrays?
Thanks in advance.
$stats = array();
foreach ($dataArray as $element) {
$obj = $element->to->data[0];
// this next line does not meet my needs, it's just to demonstrate the structure of the array
$stats[$obj->id] = array('name'=>$obj->name,'count'=>1);
// this next line obviously does not work, it's what I need to get working
$stats[$obj->id] = array('name'=>$obj->name,'count'=>++);
}
Try checking to see if your array has that value populated, if it's populated then build on that value, otherwise set a default value.
$stats = array();
foreach ($dataArray as $element) {
$obj = $element->to->data[0];
if (!isset($stats[$obj->id])) { // conditionally create array
$stats[$obj->id] = array('name'=>$obj->name,'count'=> 0);
}
$stats[$obj->id]['count']++; // increment count
}
$obj = $element->to->data is again an array. If I understand your question correctly, you would want to loop through $element->to->data as well. So your code now becomes:
$stats = array();
foreach ($dataArray as $element) {
$toArray = $element->to->data[0];
foreach($toArray as $toElement) {
// check if the key was already created or not
if(isset($stats[$toElement->id])) {
$stats[$toElement->id]['count']++;
}
else {
$stats[$toElement->id] = array('name'=>$toArray->name,'count'=>1);
}
}
}
Update:
Considering performance benchmarks, isset() is lot more faster than array_key_exists (but it returns false even if the value is null! In that case consider using isset() || array_key exists() together.
Reference: http://php.net/manual/en/function.array-key-exists.php#107786
I'm trying to convert a multidimensional object into an array to pass into an API call that requires an array. Problem is, even after converting the object to array using the lazy method of:
$data = json_decode(json_encode($object),true)
The returned value is acting funny. I'm getting empty array values where values should be none, so I tried running it through a foreach loop:
foreach ( $data as $key => $data_each ) {
if ( is_array($data_each[$key]) ) { $data[$key] = NULL; }
}
But it's not catching the array value in the foreach loop. After running the foreach, I check the value that I happen to KNOW is coming back as an empty array and as long as I check using in_array outside the foreach loop is is catching it as an array. But not within the loop for some reason.
What am I missing?
You have this:
foreach ( $data as $key => $data_each ) {
if ( is_array($data_each[$key]) ) { $data[$key] = NULL; }
}
Here, $data_each is a value of $data[$key]. Try is_array($data_each) if you want to check whether the value is an array or not. In the loop $data_each is same as $data[$key].
I need to apply some functions to a key items inside an array before moving on with the whole array, but I probably miss something.
Here is my code:
// Get generated datas
$data_post = $this->input->post('form_data'); // Need to update this array
foreach( $data_post as $data ){
$data['password'] = password_encrypt($data['password']);
var_dump($data); // Password encryption succeed
}
var_dump($data_post); // But here, the password is still the same, no encryption applied
So as commented in the CODE section, how should I update the main $data_post array with the modifications made in foreach() ?
Inside a foreach, data isn't passed by reference. This means that modifying the variable $data doesn't modify $data_post. You can modify the original array in more than one way, but here is how I would do it:
foreach( $data_post as $key => $data ){
$data_post[$key]['password'] = password_encrypt($data['password']);
}
Note that this presumes that $data_post contains multiple sub arrays, each with the password key (or else notices will be thrown).
Try this:
$data_post = $this->input->post('form_data'); // Need to update this array
foreach( $data_post as $k=>$v ){
if($k == "password"){
$data_post[$k] = password_encrypt($v);
}
}
var_dump($data_post);