Мне пришлось придумать способ преобразования ключей массива с помощью undescores (underscore_case) в camelCase. Это нужно было сделать рекурсивно, так как я не знал, какие массивы будут переданы методу.
Я придумал это:
private function convertKeysToCamelCase($apiResponseArray) { $arr = []; foreach ($apiResponseArray as $key => $value) { if (preg_match('/_/', $key)) { preg_match('/[^_]*/', $key, $m); preg_match('/(_)([a-zA-Z]*)/', $key, $v); $key = $m[0] . ucfirst($v[2]); } if (is_array($value)) $value = $this->convertKeysToCamelCase($value); $arr[$key] = $value; } return $arr; }
Он выполняет эту работу, но я думаю, что это можно сделать гораздо лучше и сжато. Несколько вызовов preg_match
а затем конкатенация просто выглядят странно.
Вы видите способ убрать этот метод? И что еще более важно, возможно ли вообще выполнить одну операцию с одним вызовом preg_match
? Как это будет выглядеть?
Рекурсивная часть не может быть упрощена или преувеличена.
Но преобразование из underscore_case (также известного как snake_case ) и camelCase может быть выполнено несколькими различными способами:
$key = 'snake_case_key'; // split into words, uppercase their first letter, join them, // lowercase the very first letter of the name $key = lcfirst(implode('', array_map('ucfirst', explode('_', $key))));
или
$key = 'snake_case_key'; // replace underscores with spaces, uppercase first letter of all words, // join them, lowercase the very first letter of the name $key = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key))));
или
$key = 'snake_case_key': // match underscores and the first letter after each of them, // replace the matched string with the uppercase version of the letter $key = preg_replace_callback( '/_([^_])/', function (array $m) { return ucfirst($m[1]); }, $key );
Выберите свой любимый!
Я могу быстро определить две отдельные задачи. Один из них – преобразовать строки в формат верблюда, а другой – сопоставить ключи многомерного массива. Эти задачи не имеют никакого отношения к другому, и поэтому лучше всего реализовать их как отдельные функции.
Давайте начнем с функции более высокого порядка mapArrayKeys
. Он примет функцию сопоставления и применит эту функцию к каждому ключу массива, создав новый массив. Мы должны ожидать, что функция отображения будет инъективной (взаимно однозначной).
function mapArrayKeys(callable $f, array $xs) { $out = array(); foreach ($xs as $key => $value) { $out[$f($key)] = is_array($value) ? mapArrayKeys($f, $value) : $value; } return $out; }
Есть несколько нерешительных бит, которые я не считаю столь важными. Возможно, вам не захочется вводить намеки на параметры, хорошо. Может быть, вы предпочтете, если / then / else вместо тернарного оператора, хорошо. Важно то, что с помощью mapArrayKeys
вы можете применить любую (инъективную) функцию сопоставления к клавишам массива.
Вторая задача – преобразовать строки в верблюд-футляр. Для этого вы можете использовать функции PCRE, это нормально. Я собираюсь использовать explode
чтобы сделать расщепление.
function underToCamel($str) { return lcfirst(implode('', array_map('ucfirst', explode('_', $str)))); }
Теперь эти две функции могут использоваться в тандеме для достижения общей цели преобразования ключей массива из подчёркивания в формат верблюда.
mapArrayKeys('underToCamel', array('foo_bar' => array ('baz_qux' => 0)));
Заметка об инъективности. Функция underToCamel
не обязательно инъективна, поэтому вам нужно проявлять особую осторожность. Вы должны предположить, что для всех x_y
и всех xY
(где Y – это xY
версия y), что ровно один из x_y
, xY
, x_Y
является x_Y
подчеркивания (то же самое следует за более подчеркиванием).
Например, underToCamel("foo_bar") == "fooBar"
и underToCamel("fooBar") == "fooBar"
и underToCamel("foo_Bar") == "fooBar"
и поэтому только один может быть допустимым underToCamel("foo_Bar") == "fooBar"
подчеркивания.
Это ответ на комментарий luqo33 .
То, что я подразумевал под «слишком сложным» (по крайней мере, на мой взгляд), заключается в том, что в этом решении используется множество вложенных функций (например, четыре функции, называемые в underToCamel, все вложенные – затрудняет чтение).
Строка кода, о которой идет речь, такова.
lcfirst(implode('', array_map('ucfirst', explode('_', $str))));
Я оспариваю, что это читаемо. Я действительно признаю, что этот стиль не типичен для PHP, и я думаю, поэтому читатель PHP может быть отложен.
Сначала следует отметить, что вложенные функции на самом деле не такие ненормальные, как вы думаете. Рассмотрим математическое выражение.
(-$b + sqrt($b*$b - 4*$a*$c)) / (2*$a)
Это выражение, которое использует множество вложенных функций: +, -, *, /
. Если вы притворяетесь, что у вас не было BEDMAS (или эквивалента), встроенного в ваше подсознание, это на самом деле сложное выражение для понимания – есть неявные правила, которые вы подсознательно применяете, чтобы знать, что сначала вы делаете материал в круглых скобках, затем умножения , и так далее. Ничто из этого не кажется сложным, потому что вы научились читать такие выражения, и теперь это часть вашего репертуара. То же самое касается чтения выражений, подобных тому, которое я использовал.
Я могу переписать выражение так, чтобы в каждой строке использовалась одна функция.
$a = explode('_', $str); $b = array_map('ucfirst', $a); $c = implode('', $b); $d = lcfirst($c);
Теперь порядок выполнения читается сверху вниз. Я также могу написать его, чтобы читать снизу вверх.
lcfirst( implode('', array_map('ucfirst', explode('_', $str ))));
Наконец, я могу написать его для чтения справа налево или изнутри (если вы считаете круглые скобки), как это было изначально написано.
lcfirst(implode('', array_map('ucfirst', explode('_', $str))));
Все эти версии используют простой шаблон, называемый функцией композиции , что является еще одной причиной, которую легко читать и понимать. С составом функций вы можете создать последовательность функций, в которых каждая функция будет подаваться с выхода предыдущей функции.
Чтобы объяснить этот сценарий, моя последовательность функций в порядке слева направо – explode '_', array_map 'ucfirst', implode '', lcfirst
. То, как это работает, может быть ясно видно из версии, которая использует переменные $a
через $d
. Вы бросаете что-то в explode '_'
, и результат от этого передается в array_map 'ucfirst'
, а затем в implode ''
и, наконец, в lcfirst
. Вы могли бы думать об этом как о трубопроводе, о сборочной линии или о чем-то подобном.
Вы можете использовать preg_replace_callback
и изменять все ключи, не array_keys
каждый из них с помощью array_keys
и array_combine
:
private function convertKeysToCamelCase($apiResponseArray) { $keys = preg_replace_callback('/_(.)/', function($m) { return strtoupper($m[1]); }), array_keys($apiResponseArray)); return array_combine($keys, $apiResponseArray); }
или без регулярного выражения:
private function convertKeysToCamelCase($apiResponseArray) { $keys = array_map(function ($i) { $parts = explode('_', $i); return array_shift($parts). implode('', array_map('ucfirst', $parts)); }, array_keys($apiResponseArray)); return array_combine($keys, $apiResponseArray); }
Вы можете изменить вторую функцию для работы с многомерными массивами:
private function convertKeysToCamelCase($apiResponseArray) { $keys = array_map(function ($i) use (&$apiResponseArray) { if (is_array($apiResponseArray[$i])) $apiResponseArray[$i] = $this->convertKeysToCamelCase($apiResponseArray[$i]); $parts = explode('_', $i); return array_shift($parts) . implode('', array_map('ucfirst', $parts)); }, array_keys($apiResponseArray)); return array_combine($keys, $apiResponseArray); }
Попробуйте preg_replace_callback()
:
$key = preg_replace_callback('/_([az]*)/', function($matches) { return ucfirst($matches[1]); }), $key);
Пара отмечает:
[az]
потому что [AZ]
уже будет заглавными *
Повторение 0+ использовалось, если мы имеем ситуацию типа camel_$case
, я предположил, что _
все еще нужно заменить ucfirst()
в первой группе совпадений Вот еще один подход, array_walk_recursive()
preg_replace_callback
методов array_walk_recursive()
и preg_replace_callback
🙂
function convertKeysToCamelCase($array) { $result = []; array_walk_recursive($array, function ($value, &$key) use (&$result) { $newKey = preg_replace_callback('/_([az])/', function ($matches) { return strtoupper($matches[1]); }, $key); $result[$newKey] = $value; }); return $result; }
Если вы не хотите делать это с помощью регулярного выражения:
+ (NSString*)camelFromUnderscore:(NSString*)underscore { NSMutableArray* components = [NSMutableArray arrayWithArray:[underscore componentsSeparatedByString:@"_"]]; NSString* firstComponent = [components objectAtIndex:0]; [components removeObjectAtIndex:0]; NSString* remainingComponents = [[[components componentsJoinedByString:@" "] capitalizedString] stringByReplacingOccurrencesOfString:@" " withString:@""]; NSString* name = [NSString stringWithFormat:@"%@%@", firstComponent, remainingComponents]; return name; }
function convertToCamelCase($array){ $finalArray = array(); foreach ($array as $key=>$value): if(strpos($key, "_")) $key = lcfirst(str_replace("_", "", ucwords($key, "_"))); //let's convert key into camelCase if(!is_array($value)) $finalArray[$key] = $value; else $finalArray[$key] = $this->_convertToCamelCase($value ); endforeach; return $finalArray; }
Конвертируемая клавиша convertToCamelCase преобразует массив ключей в корпус верблюда
Например, вы можете использовать это как:
$finalArray = convertToCamelCase($array); print_r(finalArray);