Populate an associative array from a string with the same symbol used as a delimiter and in values
Asked Answered
H

6

6

How can I convert string to an array?

Input:

$string = "12&Shoes&28&Jewelry&30&Watch&96&Beauty&98&Kids&Baby";

Desired output:

array{
    [12] => 'Shoes',
    [28] => 'Jewelry',
    [30] => 'Watch',
    [96] => 'Beauty',
    [98] => 'Kids&Baby'
}

Can anyone suggest how I can concisely convert it using a PHP function like preg_match(), preg_match_all(), etc.?

Hibbs answered 29/5, 2017 at 19:26 Comment(12)
Have you tried something?Burrow
php.net/manual/en/function.explode.phpFerocious
@Burrow Thanks But yet I have no more Idea how can I convert it.Hibbs
Explode give me like single array. like Array ( [0] => 12 [1] => Shoes [2] => 28 [3] => Jewelry [4] => 30 [5] => Watch [6] => 96 [7] => Beauty [8] => 98 [9] => Kids [10] => Baby )Hibbs
This will be very hard as the string does not follow any pattern. May be you can identify integer & string to splitBurrow
This will be very hard as the string does not follow any pattern. May be you can identify integer & string to splitBurrow
Since the string contains "Kids&Baby", I am not sure if any of the answer so far will give exact resultsBurrow
@Burrow for "Kids&Baby" we can use str_replace for it Like $string = "12&Shoes&28&Jewelry&30&Watch&96&Beauty&98&Kids&Baby"; $string = str_replace('Kids&Baby','Kids_Baby',$string); // Output will be "12&Shoes&28&Jewelry&30&Watch&96&Beauty&98&Kids_Baby" So "Kids&Baby" will be not meter.Hibbs
@RP, try my solution with preg_match_all. I'm trying to figure out how to remove the last & sign, it's been a long time regexp :)Zenithal
@RP, just posted an answer with few lines of code as you expected.Burrow
@Burrow Thanks again.Hibbs
Thanks to all to give me answer and best reply.Hibbs
P
3

I know you asked for regex, but i'm the one who likes to do other way if it's simple to do.

$string = "12&Shoes&28&Jewelry&30&Watch&96&Beauty&98&Kids&Baby&Try";
$array = explode('&', $string);
$total = count($array);
for ($i = 0; $i < $total; $i++) {
  if (is_numeric($array[$i])) {
   $result[$array[$i]] = '';
   $lastIndex = $array[$i];
  } else {
    if ($result[$lastIndex] == ''){
       $result[$lastIndex] .= $array[$i];
    } else {
        $result[$lastIndex] .= '&' . $array[$i];
    }
  }
}
var_dump($result);
Piling answered 29/5, 2017 at 19:34 Comment(7)
My first thought, but wouldn't work because of the "Kids&Baby".Sox
Great its Done. Can we optimize it?Hibbs
Yeah, im doing that, wait plzPiling
can you explain how you fixed Kids&Baby? :DGallia
@skunkhaze The previous code was complicated I simplified that, review plz. I'm at work, sry if I couldn't do bestPiling
@CarlosAlexandre I think the previous edit was way cleaner. But It's up to you how to leave it.Sox
@Sox With that way we can rely on the code all of parsing between indexes and content and only spending one more var, no much costs. The previous code wasn't optimized as R P stated above. If the string take more than 3 or 4 strings, it would be a problem. I open to reviews too, thanksPiling
O
1

Try next one:

$list = array();

// $tmp = explode('&', $string); - not suitable coz of 'Value&Value' existence 
preg_match_all('/\d+|[a-z&]+/i', $string, $tmp);
foreach ($tmp[0] as $i => $value) {
    // 0, 2, ... elements are the keys
    if ($i%2 === 0) {
        $key = $value;
    // 1, 3 ... values
    } else {
        $list[$key] = trim($value, '&');
    }
}

Or even shorter version:

preg_match_all('/(\d+)\&([a-z&]+[^\&0-9])/i', $string, $tmp);
$list = array_combine($tmp[1], $tmp[2]);

Result:

var_dump($list);

array(5) {
  [12] => string(5) "Shoes"
  [28] => string(7) "Jewelry"
  [30] => string(5) "Watch"
  [96] => string(6) "Beauty"
  [98] => string(9) "Kids&Baby"
}
Obtect answered 29/5, 2017 at 19:37 Comment(3)
Thank for reply and try. code give me Warning Like - Warning: trim() expects parameter 1 to be string, array given in D:\xampp\htdocs\test\index.php on line 11 - Warning: Illegal offset type in D:\xampp\htdocs\test\index.php on line 11Hibbs
@RP, it was true before last edit (foreach ($tmp -> foreach ($tmp[0] )Obtect
Even shorter would be: preg_match_all('/(\d+)\&([a-z&]+[^\&0-9])/i', $string, $tmp); $list = array_combine($tmp[1], $tmp[2]);Obtect
B
1

Please try PREG_SPLIT_DELIM_CAPTURE with preg_split to get the items which was split ,

$string = "12&Shoes&28&Jewelry&30&Watch&96&Beauty&98&Kids&Baby";

$array = preg_split("/(\d+)/", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
foreach($array AS $key => $val) {
   if($key%2) continue;
   $result[$val] = trim($array[$key+1], '&');
}
Burrow answered 29/5, 2017 at 20:8 Comment(0)
Z
0

I removed my previous answer, since I suggested parse_str, but I didn't read the question correctly.

One solution could be, using preg_match_all. The only problem is, I can't filter out the last & at the end of each value. Trying to figure out how regexp non-matching works again :)

NOTE! This isn't going to work if your values contain numbers!

php > preg_match_all('/(?P<key>[0-9]+)&(?P<values>[^0-9]+)/i', $string, $matches);
php > var_dump($matches);
array(5) {
  [0]=>
  array(5) {
    [0]=>
    string(9) "12&Shoes&"
    [1]=>
    string(11) "28&Jewelry&"
    [2]=>
    string(9) "30&Watch&"
    [3]=>
    string(10) "96&Beauty&"
    [4]=>
    string(12) "98&Kids&Baby"
  }
  ["key"]=>
  array(5) {
    [0]=>
    string(2) "12"
    [1]=>
    string(2) "28"
    [2]=>
    string(2) "30"
    [3]=>
    string(2) "96"
    [4]=>
    string(2) "98"
  }
  [1]=>
  array(5) {
    [0]=>
    string(2) "12"
    [1]=>
    string(2) "28"
    [2]=>
    string(2) "30"
    [3]=>
    string(2) "96"
    [4]=>
    string(2) "98"
  }
  ["values"]=>
  array(5) {
    [0]=>
    string(6) "Shoes&"
    [1]=>
    string(8) "Jewelry&"
    [2]=>
    string(6) "Watch&"
    [3]=>
    string(7) "Beauty&"
    [4]=>
    string(9) "Kids&Baby"
  }
  [2]=>
  array(5) {
    [0]=>
    string(6) "Shoes&"
    [1]=>
    string(8) "Jewelry&"
    [2]=>
    string(6) "Watch&"
    [3]=>
    string(7) "Beauty&"
    [4]=>
    string(9) "Kids&Baby"
  }
}
Zenithal answered 29/5, 2017 at 19:34 Comment(1)
My apologies, I scanned your string to fast and didn't notice it isn't a valid parameter=value combination, like you use in URL's. So my solution isn't going to work. It would have worked if you strings looks like: $string = "12=Shoes&28=Jewelry&30=Watch&96=Beauty&98=Kids&Baby";Zenithal
C
0

Here is my improvement to @FieryCat's snippets.

To accommodate Kids&Baby, just make it optional for a "value" component to contain a & if it is immediately followed by letters.

Code: (Demo)

$string = "12&Shoes&28&Jewelry&30&Watch&96&Beauty&98&Kids&Baby";

var_export(
    preg_match_all(
        '/(\d+)&([a-z]+(?:&[a-z]+)?)/i',
        $string,
        $m
    )
    ? array_combine($m[1], $m[2])
    : []
);

Output:

array (
  12 => 'Shoes',
  28 => 'Jewelry',
  30 => 'Watch',
  96 => 'Beauty',
  98 => 'Kids&Baby',
)
Catfall answered 14/5, 2022 at 10:56 Comment(0)
S
-1

Try this:

function trim_custom($item){
  return trim($item,'&');
}

$string = "12&Shoes&28&Jewelry&30&Watch&96&Beauty&98&Kids&Baby";
$values = array_filter( explode(",",preg_replace("/[^a-zA-Z-&]+/", ",", $string)) );
$values = array_map('trim_custom',$values);
$keys = array_filter ( explode(",",preg_replace("/[^0-9]+/", ",", $string)) );

$list = array_combine($keys,$values);

Output:

print_r($list);

Array
(
    [12] => Shoes
    [28] => Jewelry
    [30] => Watch
    [96] => Beauty
    [98] => Kids&Baby
)
Say answered 29/5, 2017 at 19:32 Comment(3)
Kids&Baby is one value thoughGallia
@Ali Hesari thanks. but in last "Kids&Bab" will be a problem.Hibbs
@RP I've fixed functionSay

© 2022 - 2025 — McMap. All rights reserved.