Add or modify part of an array as a mysql string - php

I currently have a column in mysql database that stores a string with delimiters so I can convert it into an array in php.
An example string looks like this:
23,1,1|72,2,0|16,3,1|...etc.
It essentially divides it into 3 major groups with |, and 3 smaller ones with , (if there's a cleaner way, let me know):
1st number is an ID number for an article from a different table
2nd is just a number for indenting purposes
3rd is for visible or not (0 or 1).
I will have an admin section where we'll be able to re-order the major groups (i.e., move group 3 to position 2) and modify specific numbers from the sub-groups (e.g. change 72,1,0 to 72,2,0) I'm not sure how I can accomplish this.
How do I loop through these modifications while keeping the order (or new order) when reinserting into the database?
I was thinking of adding a another number to my string that would determine the position of each major group? Something like this:
1[23,1,1]2[72,2,0]3[16,3,1]
But how do I loop through this and move things around?
Any help would be greatly appreciated.

I agree with the comments about normalization, but if you insist on doing it this way, or are stuck with an existing schema you cannot alter, use the PHP serialize/unserialize functions if you can, rather than string parsing. This will at least allow you to retrieve the data into PHP and modify the array and then save it back.
http://php.net/manual/en/function.serialize.php

I'm joining all comments about the approach used to store data, but nevertheless.
This what can help you to move forward:
/** #var string $s Assumed packet */
$s = "23,3,1|72,1,0|16,2,1"; // Indent is not ordered
/** #var array $parsed Parsed packet */
$parsed = array_map(function ($segment) {
list($artId, $indent, $visibility) = explode(',', $segment);
return [
'ArticleId' => (int)$artId,
'Indent' => (int)$indent,
'Visible' => (int)$visibility !== 0
];
}, explode('|', $s));
usort($parsed, function (array $a, array $b) {
return ($a['Indent'] < $b['Indent']) ? -1 : 1;
});
You'll get following $parsed structure sorted by Indent key:
array(3) {
[0] => array(3) {
["ArticleId"]=> int(23)
["Indent"]=> int(1)
["Visible"]=> bool(true)
}
[1] => array(3) {
["ArticleId"]=> int(72)
["Indent"]=> int(2)
["Visible"]=> bool(false)
}
[2] => array(3) {
["ArticleId"]=> int(16)
["Indent"]=> int(3)
["Visible"]=> bool(true)
}
}
Thus you can alter Indent as you want just applying usort() before/after parsing.
Regarding storing this structure in database the way you decided, you can use JSON format (json_encode(),json_decode()). Such "serialization" way faster than proposed serialize() method and way more faster than $parsed approach + more readable. If you worry about redundancy you can json_encode() array without array keys and add them on parsing or use directly [0], [1], [2] knowing the correspondence beforehand.
If you use json_*() functions you can omit structure parsing bec. it will be decoded right the same you've encoded it for saving. Order can be defined on save using same usort(). This can be considered as improvement by reducing excessive sorts bec. readings/decodings will occur more frequently than saves.

I was interested with your question. So i create this. It may not like what you desired.
<?php
$string = "23,1,1|72,2,0|16,3,1|";
$explode = explode("|", $string);
$a = array();
for($x = 0;$x<count($explode)-1;$x++)
{
$a[] = $explode[$x];
$b[] =explode(',',substr($explode[$x], strpos($explode[$x], ",") + 1));
}
for($y=0;$y<count($b);$y++){
echo $b[$y][0]. '=>'. $a[$y] . '<br>';
}
?>

Related

How can I change array items?

I have an array containing links. I am trying to cut a part of those links. For example:
$array = [
"https://eksisozluk.com/merve-sanayin-pizzaciya-kapiyi-ciplak-acmasi--5868043?a=popular",
"https://eksisozluk.com/merve-sanayin-pizzaciya-kapiyi-ciplak-acmasi--5868043?a=popular"
];
I want change these links like below:
array(2) {
[0]=>
string(91) "https://eksisozluk.com/merve-sanayin"
[1]=>
string(91) "https://eksisozluk.com/merve-sanayin"
[2]=>
}
Is there any possible way to edit array items?
Given the array:
$array = [
"https://eksisozluk.com/merve-sanayin-pizzaciya-kapiyi-ciplak-acmasi--5868043?a=popular",
"https://eksisozluk.com/merve-sanayin-pizzaciya-kapiyi-ciplak-acmasi--5868043?a=popular"
];
Using array_walk() (modifies the array in place).
Using a regular expression this time:
function filter_url(&$item)
{
preg_match('|(https:\/\/\w+\.\w{2,4}\/\w+-\w+)-.+|', $item, $matches);
$item = $matches[1];
}
array_walk($array, 'filter_url');
(See it working here).
Note that filter_url passes the first parameter by reference, as explained in the documentation, so changes to each of the array items are performed in place and affect the original array.
Using array_map() (returns a modified array)
Simply using substr, since we know next to nothing about your actual requirements:
function clean_url($item)
{
return substr($item, 0, 36);
}
$new_array = array_map('clean_url', $array);
Working here.
The specifics of how actually filter the array elements are up to you.
The example shown here seems kinda pointless, since you are setting all elements exactly to the same value. If you know the lenght you can use substr, or you could just could write a more robust regex.
Since all the elements of your input array are the same in the example, I am going to assume this doesn't represent your actual input.
You could also iterate the array using either for, foreach or while, but either of those options seems less elegant when you have specific array functions to deal with this kind of situation.
There are multiple ways. One way is to iterate over the items and clip them with substr().
$arr = array("https://eksisozluk.com/merve-sanayin-pizzaciya-kapiyi-ciplak-acmasi--5868043?a=popular",
"https://eksisozluk.com/merve-sanayin-pizzaciya-kapiyi-ciplak-acmasi--5868043?a=popular");
for ($i = 0; $i < count($arr); $i++)
{
$arr[$i] = substr($arr[$i], 0, 36);
}

PHP string with int or float to number

If I have two strings in an array:
$x = array("int(100)", "float(2.1)");
is there a simple way of reading each value as the number stored inside as a number?
The reason is I am looking at a function (not mine) that sometimes receives an int and sometimes a float. I cannot control the data it receives.
function convertAlpha($AlphaValue) {
return((127/100)*(100-$AlphaValue));
}
It causes an error in php
PHP Notice: A non well formed numeric value encountered
which I want to get rid of.
I could strip the string down and see what it is and do an intval/floatval but wondered if there was a neat way.
UPDATE:
Playing about a bit I have this:
function convertAlpha($AlphaValue)
{
$x = explode("(", $AlphaValue);
$y = explode(")", $x[1]);
if ($x[0] == "int") {
$z = intval($y[0]);
}
if ($x[0] == "float") {
$z = floatval($y[0]);
}
return((127/100)*(100-$z)); }
This which works but it just messy.
<?php
$x = array("int(100)", "float(2.1)");
$result = [];
foreach($x as $each_value){
$matches = [];
if(preg_match('/^([a-z]+)(\((\d+(\.\d+)?)\))$/',$each_value,$matches)){
switch($matches[1]){
case "int": $result[] = intval($matches[3]); break;
case "float": $result[] = floatval($matches[3]);
}
}
}
print_r($result);
OUTPUT
Array
(
[0] => 100
[1] => 2.1
)
The simplest would simply be to make the array as you need it, so instead of
$x = array("int(100)", "float(2.1)");
you have:
$x = [100, 2.1];
but as this is not what you want you got two choices now. One, is to use eval(), for example:
$x = ['(int)100', '(float)2.1'];
foreach ($x as $v) {
var_dump(eval("return ${v};"));
}
which will produce:
int(100)
double(2.1)
As you noticed, source array is bit different because as there is no such function in PHP as int() or float(), so if you decide to use eval() you need to change the string to be valid PHP code with the casting as shown in above example, or with use of existing intval() or floatval() functions. Finally, you can parse strings yourself (most likely with preg_match()), check for your own syntax and either convert to PHP to eval() it or just process it in your own code, which usually is recommended over using eval().
The way I would do it is by using a regex to determine the type and value by 2 seperate groups:
([a-z]+)\((\d*\.?\d*)\)
The regex captures the alphanumeric characters up and until the first (. It then looks for the characters between the ( and ) with this part: \d*\.?\d*.
The digit-part of the regex accepts input like: 12.34, .12, 12. and 123
$re = '/([a-z]+)\((\d*\.?\d*)\)/m';
$input_values = array('int(100)', 'float(2.1)');
foreach($input_values as $input) {
preg_match_all($re, $input, $matches, PREG_SET_ORDER, 0);
var_dump($matches);
}
Which leads to the output below. As you can see, there is the type in the [1] slot and the number in the [2] slot of the array
array(1) {
[0]=>
array(3) {
[0]=>
string(8) "int(100)"
[1]=>
string(3) "int"
[2]=>
string(3) "100"
}
}
array(1) {
[0]=>
array(3) {
[0]=>
string(10) "float(2.1)"
[1]=>
string(5) "float"
[2]=>
string(3) "2.1"
}
}
You can then use a check to perform the casting like:
$value;
if(matches[1] === "int") {
$value = intval($matches[2]);
} elseif (matches[1] === "float") {
$value = floatval($matches[2]);
}
The latter code still needs error handling, but you get the idea. Hope this helps!
PHP is historically typed against strings so it's pretty strong with cases like these.
$x = array("int(100)", "float(2.1)");
^^^ ^^^
You can actually turn each of those strings into a number by multiplying the substring starting after the first "(" with just one to turn it into a number - be it integer or float:
$numbers = array_map(function($string) {
return 1 * substr(strstr($string, '('), 1);
}, $x);
var_dump($numbers);
array(2) {
[0] =>
int(100)
[1] =>
double(2.1)
}
That is PHP takes everthing numberish from a string until that string seems to end and will calculate it into either an integer or float (var_dump() shows float as double). It's just consuming all number parts from the beginning of the string.
Not saying existing answers are wrong per-se, but if you ask that as a PHP question, PHP has a parser very well for that. My suggestion is to just remove the superfluous information from the beginning of the string and let PHP do the rest.
If you want it more precise, regular expressions are most likely your friend as in the yet top rated answer, still combined with PHP will give you full checks on each string:
$numbers = array_map(function($string) {
$result = preg_match('~^(?:int|float)\(([^)]+)\)$~', $string, $group) ? $group[1] : null;
return null === $result ? $result : 1 * $result;
}, $x);
So all non int and float strings will be turned into NULLs (if any).

PHP unset vs array_pop?

If I want to remove the last element of an array, I can use either of these two code:
array_pop($array); (the return value is not used)
unset($array[count($array) -1]);
Is there any performance or semantic difference between them?
If not, which is preferred?
unset is no good if you need to "do" anything with the removed value (unless you have previously assigned it to something elsewhere) as it does not return anything, whereas array_pop will give you the thing that was the last item.
The unset option you have provided may be marginally less performant since you are counting the length of the array and performing some math on it, but I expect the difference, if any, is negligible.
As others have said, the above is true if your array is numerical and contiguous, however if you array is not structured like this, stuff gets complicated
For example:
<?php
$pop = $unset = array(
1 => "a",
3 => "b",
0 => "c"
);
array_pop($pop);
// array looks like this:
// 1 => "a", 3 => "b"
$count = count($unset) - 1;
unset($count);
// array looks like this because there is no item with index "2"
// 1 => "a", 3 => "b", 0 => "c"
array_pop($array) removes the last element of $array.
unset($array[count($array) -1]); removes the element at index count($array) -1. This element is not neccesarily the last element of the array.
Consider $array = array(0 => 'foo', 2 => 'bar', 1 => 'baz'). In this case , $array[1] is the last element. The code
foreach (array(0 => "foo", 2 => "bar", 1 => "baz") as $key => $value)
echo "$key => $value\n";
prints
0 => foo
2 => bar
1 => baz
Moreover, an element at index count($array) -1 might not even exist. There can be gaps in the set of indices and integer indices can be mixed with string indices.
The return values are different. array_pop returns the last element, while unset doesn't return anything.
For simply removing the last element, array_pop would be better because you don't need to execute count($array)-1, and it is cleaner and more readable.
Yes there is.
Firstly, the unset() option will only work for numerical, contiguous arrays. If your array contains elements that are not numerical, or has any gaps in its numerical sequence, then the unset() call will get the incorrect value from count() and will fail.
Secondly, assuming your array is numerical and contiguous, there is still a difference:
array_pop() will also give you back the value of the popped element as a return value. unset() will not do this.
So if you need to keep using the data, use array_pop().
If you don't need to keep the value, then no, it probably doesn't matter too much which one you use, I suspect that array_pop() may be faster (due to not needing to call count()), but I haven't checked, and to be honest, unless you're doing thousands of calls, the difference will be negligible anyway.
Except for the obvious differences in call syntax and return value...
array_pop always pops whatever is last.
Your count - 1 unsets an element by its numeric id, which only works as you expect it to if all elements are continuously numerically indexed.
For what it's worth, using a bit of existing code that gets called a bit over 2000 times in a run, I put in a $whatevers[]=$whatever (a parameter value) at the top and and array_pop($whatevers) at the bottom.
The function calls itself recursively down to about 7 or 8 levels and (of course) I made $whatevers static so the array grew and shrunk.
The result? The difference between this code in and commented out was unmeasurable down to 100ths of a second on a windows 7 laptop. It varied a fair bit because of other things, but over lots of runs the difference in the averages was meaningless.
The performance overhead of array_pop() just isn't worth a second thought and though unset might be theoretically faster, nobody will ever be able to detect the difference.
As others have mentioned - their functionality is the same, bar the return value from array_pop. However, it's also worth mentioning the possible performance issue of the unset method on a large array due to the count() call.
As Oswald mentions, it is also worth noting that unset() will only be working as expected on numeric keys.
Yes there is a difference
array_pop() will also return removed element eg: last element, and
unset() will not return any thing
I would prefer unset() but you call count() which can consume performance.
An alternative choice is array_slice(array, offset, length, preserve_keys) :
$array = array_slice($array, 0, -1, true);
Another consideration to take into account is that if after deleting the last item you push a new element, you get different results in which the index the new element is placed at:
unset
php > $a = [1,2,3];
php > var_dump($a);
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
php > unset($a[2]);
php > var_dump($a);
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
php > $a[] = 5;
php > var_dump($a);
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[3]=>
int(5)
}
As you see, the new element is placed at index 3 instead of 2.
array_pop
php > $a = [1,2,3];
php > var_dump($a);
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
php > array_pop($a);
php > var_dump($a);
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
php > $a[] = 5;
php > var_dump($a);
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(5)
}
Now the new element is placed at index 2. Maybe this is the most desirable behaviour.

php's explode array index

Using php's explode with the following code
$data="test_1, test_2, test_3";
$temp= explode(",",$data);
I get an array like this
array('0'=>'test_1', '1'=>'test_2', 2='test_3')
what I would like to have after explode
array('1'=>'test_1', '2'=>'test_2', 3='test_3')
You could something like this :
$temp = array_combine(range(1, count($temp)), array_values($temp));
uses array_combine() to create array using your existing values (using array_values()) and range() to create a new range from 1 to the size of your array
Working example here
Array indexes start at 0. If you really wanted to have the array start with one, you could explode it, then copy it to another array with your indexes defined as starting at 1 - but why?
You could use this possibly (untested)
$newArray=array_unshift($temp,' ');
unset($newArray[0]);
it is impossible using explode.
for($i = count($temp) - 1; $i >= 0; $i--)
$temp[$i + 1] = $temp[$i];
unset($temp[0]);
You couldn't do it directly, but this will do what you're after:
$temp=explode(',',',' . $data);
unset($temp[0]);
var_dump($temp);
array(3) {
[1]=>
string(6) "test_1"
[2]=>
string(7) " test_2"
[3]=>
string(7) " test_3"
}
Lloyd Watkin's answer makes the fewest function calls to achieve the expected result. It is certainly a superior answer to ManseUK's method which uses four functions in its one-liner after the string has been exploded.
Since this question is nearly 5 years, old there should be something valuable to add if anyone dare to chime in now...
I have two things to address:
The OP and Lloyd Watkins both fail to assign the correct delimiter to their explode method based on the sample string. The delimiter should be a comma followed by a space.
Sample Input:
$data="test_1, test_2, test_3";
No one has offered a one-liner solution that matches Lloyd's two-function approach. Here that is: (Demo)
$temp=array_slice(explode(", ",", $data"),1,null,true);
This two-function one-liner prepends the $data string with a comma then a space before exploding it. Then array_slice ignores the first empty element, and returns from the second element (1) to the end (null) while preserving the keys (true).
Output as desired:
array(
1 => 'test_1',
2 => 'test_2',
3 => 'test_3',
)

Determine whether an array is associative (hash) or not [duplicate]

This question already has answers here:
How to check if PHP array is associative or sequential?
(60 answers)
Closed last year.
I'd like to be able to pass an array to a function and have the function behave differently depending on whether it's a "list" style array or a "hash" style array. E.g.:
myfunc(array("One", "Two", "Three")); // works
myfunc(array(1=>"One", 2=>"Two", 3=>"Three")); also works, but understands it's a hash
Might output something like:
One, Two, Three
1=One, 2=Two, 3=Three
ie: the function does something differently when it "detects" it's being passed a hash rather than an array. Can you tell I'm coming from a Perl background where %hashes are different references from #arrays?
I believe my example is significant because we can't just test to see whether the key is numeric, because you could very well be using numeric keys in your hash.
I'm specifically looking to avoid having to use the messier construct of myfunc(array(array(1=>"One"), array(2=>"Two"), array(3=>"Three")))
Pulled right out of the kohana framework.
public static function is_assoc(array $array)
{
// Keys of the array
$keys = array_keys($array);
// If the array keys of the keys match the keys, then the array must
// not be associative (e.g. the keys array looked like {0:0, 1:1...}).
return array_keys($keys) !== $keys;
}
This benchmark gives 3 methods.
Here's a summary, sorted from fastest to slowest. For more informations, read the complete benchmark here.
1. Using array_values()
function($array) {
return (array_values($array) !== $array);
}
2. Using array_keys()
function($array){
$array = array_keys($array); return ($array !== array_keys($array));
}
3. Using array_filter()
function($array){
return count(array_filter(array_keys($array), 'is_string')) > 0;
}
PHP treats all arrays as hashes, technically, so there is not an exact way to do this. Your best bet would be the following I believe:
if (array_keys($array) === range(0, count($array) - 1)) {
//it is a hash
}
No, PHP does not differentiate arrays where the keys are numeric strings from the arrays where the keys are integers in cases like the following:
$a = array("0"=>'a', "1"=>'b', "2"=>'c');
$b = array(0=>'a', 1=>'b', 2=>'c');
var_dump(array_keys($a), array_keys($b));
It outputs:
array(3) {
[0]=> int(0) [1]=> int(1) [2]=> int(2)
}
array(3) {
[0]=> int(0) [1]=> int(1) [2]=> int(2)
}
(above formatted for readability)
My solution is to get keys of an array like below and check that if the key is not integer:
private function is_hash($array) {
foreach($array as $key => $value) {
return ! is_int($key);
}
return false;
}
It is wrong to get array_keys of a hash array like below:
array_keys(array(
"abc" => "gfb",
"bdc" => "dbc"
)
);
will output:
array(
0 => "abc",
1 => "bdc"
)
So, it is not a good idea to compare it with a range of numbers as mentioned in top rated answer. It will always say that it is a hash array if you try to compare keys with a range.
Being a little frustrated, trying to write a function to address all combinations, an idea clicked in my mind: parse json_encode result.
When a json string contains a curly brace, then it must contain an object!
Of course, after reading the solutions here, mine is a bit funny...
Anyway, I want to share it with the community, just to present an attempt to solve the problem from another prospective (more "visual").
function isAssociative(array $arr): bool
{
// consider empty, and [0, 1, 2, ...] sequential
if(empty($arr) || array_is_list($arr)) {
return false;
}
// first scenario:
// [ 1 => [*any*] ]
// [ 'a' => [*any*] ]
foreach ($arr as $key => $value) {
if(is_array($value)) {
return true;
}
}
// second scenario: read the json string
$jsonNest = json_encode($arr, JSON_THROW_ON_ERROR);
return str_contains($jsonNest, '{'); // {} assoc, [] sequential
}
NOTES
php#8.1 is required, check out the gist on github containing the unit test of this method + Polyfills (php>=7.3).
I've tested also Hussard's posted solutions, A & B are passing all tests, C fails to recognize: {"1":0,"2":1}.
BENCHMARKS
Here json parsing is ~200 ms behind B, but still 1.7 seconds faster than solution C!
What do you think about this version? Improvements are welcome!

Categories