regex to parse a string with known string seprarators - php

I'm trying to split a concatenated string of key1value1key2value2
The problem is I can't know in which order they are
$k = preg_split("/(name|age|sex)/", "nameJohnage27sexM");
var_dump($k);
$k = preg_split("/(sex|name|age)/", "age27sexM");
var_dump($k);
So I can't know if the age or name will be 1st or 2nd index of $k, don't even know also if "name" key is in the string, there can be a limited set of key
How to do?
edit: solved like this, tx mario
for ($i=1, $n=count($k)-1; $i<$n; $i+=2) {
$s[$k[$i]] = $k[$i+1];
}
var_dump($s);

This somewhat clumsy pattern will return a key-value list:
/(?:(name|age|sex)(.+?(?=(?:name|age|sex|\z))))/g
Thus preg_match using the above on "nameJohnage27sexM" should return the array
["name", "John", "age", "27", "sex", "MAN"]
This makes it possible to create the array ["name" => "John", ...] by iterating over the elements above.

Related

get object key ending in highest number but not numerical

I have an stdclass object like this:
{
"type": "photo",
"photo": {
"id": id,
"album_id": album_id,
"owner_id": owner_id,
"photo_75": "https://example.com/random_unique_string/random_unique_name.jpg",
"photo_130": "https://example.com/random_unique_string/random_unique_name.jpg",
"photo_604": "https://example.com/random_unique_string/random_unique_name.jpg",
"photo_807": "https://example.com/random_unique_string/random_unique_name.jpg",
"photo_1280": "https://example.com/random_unique_string/random_unique_name.jpg",
"photo_2560": "https://example.com/random_unique_string/random_unique_name.jpg",
"width": 2560,
"height": 1440,
"text": "",
"date": 1517775329,
"access_key": "key"
}
The more complete look is on this pic:
I have my code structure like this:
foreach ( $response->posts as $key => $element ) {
if ( isset ($element->attachments) ) {
foreach ( $element->attachments as $key_att => $attachment ) {
if ( $attachment->type == 'photo' ) {
//get the value of the photo with the maximum resolution
}
}
}
}
How do I get only the value of photo_2560 in this case, pointing it to the photo_ ending with the maximum numeric value? max only works with completely numeric keys... Perhaps would need a regex, but I'm weak at that. Thanks for the help.
P. S. I asked about an array and not an object initially, so the answers given are valid, I made a mistake myself. The initial name of the question was get array key ending in highest number but not numerical. I then edited it to reflect my specific issue. Sorry about the confusion.
Here is what I would do using the little known preg_grep function:
$array = [
"photo_75"=> "https=>//example.com/random_unique_string/random_unique_name.jpg",
"photo_130"=> "https=>//example.com/random_unique_string/random_unique_name.jpg",
"photo_604"=> "https=>//example.com/random_unique_string/random_unique_name.jpg",
"photo_807"=> "https=>//example.com/random_unique_string/random_unique_name.jpg",
"photo_1280"=> "https=>//example.com/random_unique_string/random_unique_name.jpg",
"photo_2560"=> "https=>//example.com/random_unique_string/random_unique_name.jpg",
"width"=> 2560,
"height"=> 1440,
"text"=> "",
"date"=> 1517775329,
"access_key"=> "key"
];
$array = preg_grep('/^photo_\d+$/', array_keys($array));
sort($array, SORT_NATURAL);
print_r(end($array));
Output
photo_2560
Sandbox
Preg Grep lets you search an array using a regular expression, It's sort of like using array filter and preg_match
$array = array_filter($array, function($item){
return preg_match('/^photo_\d+$/', $item);
});
But obviously much shorter. Like array filter it's mostly for use against the values and not the keys, but we can use array_keys to get around this. Array Keys returns an array of the keys as the new arrays value array(0=>'key', ..) which is exactly what we want.
UPDATE
Based on this comment:
Is there an alternative of array_keys for an object? Because I confused it with an array, unfortunately.
No but you can cast it (array)$obj to an array if the properties are public. We can demonstrate this easily:
class foo{
public $bar = 'hello';
}
print_r(array_keys((array)(new foo)));
Output
array(
0 => 'bar' //which is the key 'bar' or the property named $bar
)
Sandbox
While it's not "ideal" it will work.
UPDATE1
I made edits to my question, please take a look. I don't understand how to apply your example in this case :(
I think it's $attachment->photo in your code, it's really hard to tell in the image. It's whereever the 'stuff' at the very top of you question came from, your example data.
In anycase with your code you would do something like this:
foreach ( $response->posts as $key => $element ) {
if ( isset ($element->attachments) ) {
foreach ( $element->attachments as $key_att => $attachment ) {
if ( $attachment->type == 'photo' ) {
//new code
$array = preg_grep('/^photo_\d+$/', array_keys((array)$attachment->photo));
sort($array, SORT_NATURAL);
$result = end($array);
print_r($result);
}
} //end foreach
}
}//end foreach
By the way the Regex I am using ^photo_\d+$ is basically this
^ match the start of the string
photo_ match "photo_" litterally
\d+ match one or more digits
$ match the end of the string.
Note the ^ can have different meaning depending where it is, for example if its in a character class [0-9] (A range of characters 0 to 9 same as \d) like this [^0-9] it means NOT so it makes the character class match everything but what is in it. Or "negation". Which is a bit confusing, but that is how it works. In this case it would be anything NOT a digit.
By using the ^ and $ we are saying that our Regex must match the whole string. So we can avoid things like somephoto_79890_aab which if we didn't have the start and end markers our Regex photo_\d+ would match this part some[photo_79890]_aab.
Cheers.
Regex won't tell you what the highest number is. But, these "photo_N" keys can be sorted using natural order.
You didn't mention the name of your variable, so I'll just assume $array here for the sake of convenience.
Let's first get only the "photo_N" elements from the array:
$photos = array_filter(
$array['photo'],
function($key) {
return substr($key, 0, 6) === "photo_";
},
ARRAY_FILTER_USE_KEY
);
We can now sort the result of that by key, using natural order:
ksort($photos, SORT_NATURAL);
This should have put the photo with the highest numerical key at the end, so you can get its value using:
$photo = end($photos);

Get Element at N position in an Array without Loop

How get you get element key and value of an at the n position array at a particular position without loop.
Imagine
$postion = 3; // get array at 3rd position
$array = array(
"A" => "Four",
"B" => "twp",
"C" => "three",
"D" => "Four",
"E" => "Five",
"F" => "Four");
$keys = array_keys($array);
$value = array_values($array);
echo implode(array_slice($keys, $postion, 1)), PHP_EOL; // Key at 3rd posstion
echo implode(array_slice($value, $postion, 1)), PHP_EOL; // Value at n position
Output
D
Four
Issues With the method is
Multiple Duplication of the array resulting higher memory usage
Why not use loop
You have to get multiple position multiple times .. looping large data set not efficient either
Why not use a Database
Yes working with memory based database like Redis can make life easier but am particular array optimisation
Why not use SplFixedArray
This would have been solution but i the follow weer because am not using positive keys ( I really this is nor fair on php part)
Fatal error: Uncaught exception 'InvalidArgumentException'
with message 'array must contain only positive integer keys'
What do you mean by large data set :
Actually i stumble on this issue when trying to as this question Managing mega Arrays in PHP so am looking at 1e6 or 1e7 with 512M memory limit
Am sure something like fseek for array would do the trick .. but not sure if that exists
Assuming PHP 5.4, with array dereferencing:
echo $array[array_keys($array)[$position]];
In earlier versions you need to break it into two lines:
$keys = array_keys($array);
echo $array[$keys[$position]];
It would also be worth using the two-line approach in 5.4+ if you have to access multiple elements, to allow you to only call the relatively expensive array_keys() function once. Also the dereferencing approach assumes that the specific position within the array exists, which it may not. Breaking it into multiple operations would allow you to handle that error case.
Although of course you don't ever need access to the key, you can simply do:
echo array_values($array)[$position];
// or
$values = array_values($array);
echo $values[$position];
Edit
The ArrayIterator class can also do this for you:
$iterator = new ArrayIterator($array);
$iterator->seek($position);
echo $iterator->key(), " = ", $iterator->current(); // D = Four
This is probably the least expensive way to do this assuming it doesn't create a copy of the array in memory when you do it (still researching this element), and likely the best method for multiple accesses of arbitrary keys.
What you want is not possible. PHP's arrays have efficient access by key, but don't have efficient access by offset. The order is only available as a linked list, so the best efficiency you can hope for is an O(n) loop, which just goes through the array and looks for the offset:
$i = 0;
foreach ($array as $value) {
if ($i++ === $offset) {
// found value
}
}
If you want this operation to be fast, then you'll have to use a proper, numerically and sequentially indexed array.
in fact you don't need the $values array:
$keys = array_keys($array);
$value_3=$array[$keys[3]];
I dont understand your question well but if you need a key and element from position
$position = 3; // get array at 3rd position
$array = array(
"A" => "Four",
"B" => "twp",
"C" => "three",
"D" => "Four",
"E" => "Five",
"F" => "Four");
$keys = array_keys($array);
$values = array_values($array);
if($values[$position] == "Four" && $keys[$position] == "D") {
echo "All it's Right!\n";
}
you dont need implode for that task

How To Create A New Array Of First Names From Array Of First And Last Names?

Hey all. I need to create an array of first names from another array of first and last names. So basically the original array with first and last names looks like this:
Original Array
array("John Doe", "Jane Doe", "John Darling", "Jane Darling");
And the array I need to create from that would be:
New Array
array("John", "Jane", "John", "Jane");
So I guess for each value I would need to remove whatever comes after the space. If someone can point me in the correct direction to use regular expressions or some array function that would be great.
Cheers!
Update:
Thank you all for your answers.
You can create a callback function that strips the last names, and map it to the array, like so (I use a different approach rather than explode(), both are equally valid methods):
function strip_last_name($name) {
if (($pos = strpos($name, ' ')) !== false) {
// If there is at least one space present, assume 'first<space>last'
return substr($name, 0, $pos);
} else {
// Otherwise assume first name only
return $name;
}
}
$first_names = array_map('strip_last_name', $full_names);
This preserves the array keys and the order of elements.
this should do the trick, and if it shouldn't I'm really sorry but haven't slept this night :P, but you'll get the idea ;)
<?php
$secondarray=array();
foreach($firstarray as $fullname){
$s=explode(' ',$fullname);
$secondarray[]=$s[0];
}
?>
$array1 = array("John Doe", "Jane Doe", "John Darling", "Jane Darling");
$array2 = array();
foreach ($array1 as $name) {
$firstname = explode(' ',$name);
$array2[] = $firstname[0];
}
The explode function splits a string by a delimiter, for example explode('b','aaaaabccccc') returns array('aaaaa','ccccc'). The above code uses this, with a foreach loop, to split each full name into an array containing the first and last name, and then add the first name to an array like the first one.

How can I force PHP to use strings for array keys? [duplicate]

This question already has answers here:
A numeric string as array key in PHP
(11 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I've come across an old app that uses an id to name type array, for example...
array(1) {
[280]=>
string(3) "abc"
}
Now I need to reorder these, and a var_dump() would make it appear that that isn't going to happen while the keys are integers.
If I add an a to every index, var_dump() will show double quotes around the key, my guess to show it is now a string...
array(1) {
["280a"]=>
string(3) "abc"
}
This would let me easily reorder them, without having to touch more code.
This does not work.
$newArray = array();
foreach($array as $key => $value) {
$newArray[(string) $key] = $value;
}
A var_dump() still shows them as integer array indexes.
Is there a way to force the keys to be strings, so I can reorder them without ruining the array?
YOU CAN'T!!
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
Edit:
ACTUALLY YOU CAN!!
Cast sequential array to associative array
$obj = new stdClass;
foreach($array as $key => $value){
$obj->{$key} = $value;
}
$array = (array) $obj;
In most cases, the following quote is true:
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
This examples from the PHP Docs
<?php
$array = array(
1 => "a",
"1" => "b",
1.5 => "c",
true => "d",
);
var_dump($array);
?>
The above example will output:
array(1) {
[1]=> string(1) "d"
}
So even if you were to create an array with numbered keys they would just get casted back to integers.
Unfortunately for me I was not aware of this until recently but I thought I would share my failed attempts.
Failed attempts
$arr = array_​change_​key_​case($arr); // worth a try.
Returns an array with all keys from array lowercased or uppercased. Numbered indices are left as is.
My next attempts was to create a new array by array_combineing the old values the new (string)keys.
I tried several ways of making the $keys array contain numeric values of type string.
range("A", "Z" ) works for the alphabet so I though I would try it with a numeric string.
$keys = range("0", (string) count($arr) ); // integers
This resulted in an array full of keys but were all of int type.
Here's a couple of successful attempts of creating an array with the values of type string.
$keys = explode(',', implode(",", array_keys($arr))); // values strings
$keys = array_map('strval', array_keys($arr)); // values strings
Now just to combine the two.
$arr = array_combine( $keys, $arr);
This is when I discovered numeric strings are casted to integers.
$arr = array_combine( $keys, $arr); // int strings
//assert($arr === array_values($arr)) // true.
The only way to change the keys to strings and maintain their literal values would be to prefix the key with a suffix it with a decimal point "00","01","02" or "0.","1.","2.".
You can achieve this like so.
$keys = explode(',', implode(".,", array_keys($arr)) . '.'); // added decimal point
$arr = array_combine($keys, $arr);
Of course this is less than ideal as you will need to target array elements like this.
$arr["280."]
I've created a little function which will target the correct array element even if you only enter the integer and not the new string.
function array_value($array, $key){
if(array_key_exists($key, $array)){
return $array[ $key ];
}
if(is_numeric($key) && array_key_exists('.' . $key, $array)){
return $array[ '.' . $key ];
}
return null;
}
Usage
echo array_value($array, "208"); // "abc"
Edit:
ACTUALLY YOU CAN!!
Cast sequential array to associative array
All that for nothing
You can append the null character "\0" to the end of the array key. This makes it so PHP can't interpret the string as an integer. All of the array functions (like array_merge()) work on it. Also not even var_dump() will show anything extra after the string of integers.
Example:
$numbers1 = array();
$numbers2 = array();
$numbers = array();
$pool1 = array(111, 222, 333, 444);
$pool2 = array(555, 666, 777, 888);
foreach($pool1 as $p1)
{
$numbers1[$p1 . "\0"] = $p1;
}
foreach($pool2 as $p2)
{
$numbers2[$p2 . "\0"] = $p2;
}
$numbers = array_merge($numbers1, $numbers2);
var_dump($numbers);
The resulting output will be:
array(8) {
["111"] => string(3) "111"
["222"] => string(3) "222"
["333"] => string(3) "333"
["444"] => string(3) "444"
["555"] => string(3) "555"
["666"] => string(3) "666"
["777"] => string(3) "777"
["888"] => string(3) "888"
}
Without the . "\0" part the resulting array would be:
array(8) {
[0] => string(3) "111"
[1] => string(3) "222"
[2] => string(3) "333"
[3] => string(3) "444"
[4] => string(3) "555"
[5] => string(3) "666"
[6] => string(3) "777"
[7] => string(3) "888"
}
Also ksort() will also ignore the null character meaning $numbers[111] and $numbers["111\0"] will both have the same weight in the sorting algorithm.
The only downside to this method is that to access, for example $numbers["444"], you would actually have to access it via $numbers["444\0"] and since not even var_dump() will show you there's a null character at the end, there's no clue as to why you get "Undefined offset". So only use this hack if iterating via a foreach() or whoever ends up maintaining your code will hate you.
Use an object instead of an array $object = (object)$array;
EDIT:
I assumed that if they are integers, I
can't reorder them without changing
the key (which is significant in this
example). However, if they were
strings, I can reorder them how they
like as the index shouldn't be
interpreted to have any special
meaning. Anyway, see my question
update for how I did it (I went down a
different route).
Actually they dont have to be in numeric order...
array(208=>'a', 0=> 'b', 99=>'c');
Is perfectly valid if youre assigning them manually. Though i agree the integer keys might be misinterpreted as having a sequential meaning by someone although you would think if they were in a non-numeric order it would be evident they werent. That said i think since you had the leeway to change the code as you updated that is the better approach.
Probably not the most efficient way but easy as pie:
$keys = array_keys($data);
$values = array_values($data);
$stringKeys = array_map('strval', $keys);
$data = array_combine($stringKeys, $values);
//sort your data
I was able to get this to work by adding '.0' onto the end of each key, as such:
$options = [];
for ($i = 1; $i <= 4; $i++) {
$options[$i.'.0'] = $i;
}
Will return:
array("1.0" => 1, "2.0" => 2, "3.0" => 3, "4.0" => 4)
It may not be completely optimal but it does allow you to sort the array and extract (an equivalent of) the original key without having to truncate anything.
Edit:
This should work
foreach($array as $key => $value) {
$newkey = sprintf('%s',$key);
$newArray["'$newkey'"] = $value;
}
Hi we can make the index of the array a string using the following way. If we convert an array to xml then indexes like [0] may create issue so convert to string like [sample_0]
$newArray = array();
foreach($array as $key => $value) {
$newArray["sample_".$key] = $value;
}
All other answers thus far are hacks that either use fragile workarounds that could break between major PHP versions, create unnecessary gotchas by deliberately corrupting keys, or just slow down your code for no benefit. The various functions to sort arrays yet maintain the crucial key associations have existed since PHP 4.
It is pointless stop PHP from using integer keys, it only does so when the integer representation is exactly the same as the string, thus casting an integer key back to string when reading from the array is guaranteed to return the original data. PHP's internal representation of your data is completely irrelevant as long as you avoid the functions that rewrite integer keys. The docs clearly state which array functions will do that.
An example of sorting, without any hacks, that demonstrates how data remains uncorrupted:
<?php
# use string keys to define as populating from a db, etc. would,
# even though PHP will convert the keys to integers
$in = array(
'347' => 'ghi',
'176' => 'def',
'280' => 'abc',
);
# sort by key
ksort($in);
echo "K:\n";
$i = 1;
foreach ($in as $k => $v) {
echo $i++, "\n";
$k = (string) $k; # convert back to original
var_dump($k, $v);
}
# sort by value
asort($in, SORT_STRING);
echo "\nV:\n";
$i = 1;
foreach ($in as $k => $v) {
echo $i++, "\n";
$k = (string) $k;
var_dump($k, $v);
}
# unnecessary to cast as object unless keys could be sequential, gapless, and start with 0
if (function_exists('json_encode')) {
echo "\nJSON:\n", json_encode($in);
}
The output it produces hasn't changed since v5.2 (with only the JSON missing prior to that):
K:
1
string(3) "176"
string(3) "def"
2
string(3) "280"
string(3) "abc"
3
string(3) "347"
string(3) "ghi"
V:
1
string(3) "280"
string(3) "abc"
2
string(3) "176"
string(3) "def"
3
string(3) "347"
string(3) "ghi"
JSON:
{"280":"abc","176":"def","347":"ghi"}

Given an array of arrays, how can I strip out the substring "GB" from each value?

Each item in my array is an array of about 5 values.. Some of them are numerical ending in "GB".. I need the same array but with "GB" stripped out so that just the number remains.
So I need to iterate through my whole array, on each subarray take each value and strip the string "GB" from it and create a new array from the output.
Can anyone recommend and efficient method of doing this?
You can use array_walk_recursive() for this:
array_walk_recursive($arr, 'strip_text', 'GB');
function strip_text(&$value, $key, $string) {
$value = str_replace($string, '', $value);
}
It's a lot less awkward than traversing an array with its values by reference (correctly).
You can create your own custom function to iterate through an array's value's, check if the substring GB exists, and if so, remove it. With that function you can pass the original array and the function into array_map
// i only did the subarray part
$diskSizes = array("500", "300 GB", "200", "120 GB", "130GB");
$newArray = array();
foreach ($diskSizes as $diskSize) {
$newArray[] = str_replace('GB', '', $diskSize);
}
// look at the new array
print_r($newArray);
$arr = ...
foreach( $arr as $k => $inner ) {
foreach( $inner as $kk => &$vv ) {
$vv = str_replace( 'GB', '', $vv );
}
}
This actually keeps the original arrays intact with the new strings. (notice &$vv, which means that i'm getting the variable by reference, which means that any changes to $vv within the loop will affect the actual string, not by copying)

Categories