LoginSignup
6
6

More than 5 years have passed since last update.

PHPで動的n次元配列追加をしたいです。 例え…

Posted at

流行りに乗ってみた。

結論

もうちょっと仕様が明確にならないと厳しい。
ぶっちゃけparserの方が重要だよねこれ。

お題

PHPで動的n次元配列追加をしたいです。例えば以下のように明示的に['a']['b']... - Yahoo!知恵袋

実装

再利用が出来るのでそれなりに画期的ではなかろうか。

<?php

echo str_repeat('=', 36), PHP_EOL;
echo "お題:a((b((c)))) => ['a' => [['b'=>[['c']]]]]", PHP_EOL;
echo str_repeat('=', 36), PHP_EOL;
echo "['a' => [['b'=>[['c']]]]]を実際展開するとこうなる。", PHP_EOL;
var_dump(['a' => [['b'=>[['c']]]]]);
echo PHP_EOL;

//超☆手抜きparser
$string = 'a((b((c))))';
$keys = explode('(', str_replace(')', '', $string));
$keys = array_map(function ($value) {return $value === '' ? 0 : $value;}, $keys);
$value = array_pop($keys);
$keys[] = 0;

//一括DONE
$result = set_lowest([], $keys, $value);

echo PHP_EOL;
echo "ウチの実行結果。", PHP_EOL;
var_dump($result);
echo PHP_EOL;

/**
 * 指定された階層にある値を設定します。
 *
 * @param   array   $array  配列
 * @param   mixed   $keys   階層
 * @return  array   設定後の配列
 */
function set_lowest ($array, $keys, $value) {
    $keys = (array) $keys;
    if (empty($array)) {
        $tmp =& $array;
    } else {
        $tmp =& $array[array_shift($keys)];
    }

    foreach ($keys as $key) {
        if (!isset($tmp[$key])) {
            $tmp[$key] = null;
        }
        $tmp =& $tmp[$key];
    }
    $tmp = $value;
    return $array;
}

実行結果

====================================
お題:a((b((c)))) => ['a' => [['b'=>[['c']]]]]
====================================
['a' => [['b'=>[['c']]]]]を実際展開するとこうなる。
array(1) {
  ["a"]=>
  array(1) {
    [0]=>
    array(1) {
      ["b"]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "c"
        }
      }
    }
  }
}


ウチの実行結果。
array(1) {
  ["a"]=>
  array(1) {
    [0]=>
    array(1) {
      ["b"]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          string(1) "c"
        }
      }
    }
  }
}

手前味噌

大分前にこんなクラスを作っていたわけで。

関数の元ネタのArrays::SetLowest()は商用環境での実績もあるのでまぁ使用は許されるのではなかろうか。

Arrays.php
<?php
/**  ______ _                 _               _ ___
 *  |  ____| |               | |             | |__ \
 *  | |__  | |_   ___      __| |__   ___  ___| |  ) |
 *  |  __| | | | | \ \ /\ / /| '_ \ / _ \/ _ \ | / /
 *  | |    | | |_| |\ V  V / | | | |  __/  __/ |/ /_
 *  |_|    |_|\__, | \_/\_/  |_| |_|\___|\___|_|____|
 *             __/ |
 *            |___/
 *
 * Flywheel2: the inertia php framework
 *
 * @category    Flywheel2
 * @package     vartype
 * @author      wakaba <wakabadou@gmail.com>
 * @copyright   2011- Wakabadou honpo (http://www.wakabadou.net/) / Project ICKX (http://www.ickx.jp/)
 * @license     http://opensource.org/licenses/MIT The MIT License MIT
 * @varsion     2.0.0
 */

namespace ickx\fw2\vartype\arrays;

/**
 * 配列ユーティリティです。
 *
 * @category    Flywheel2
 * @package     vartype
 * @author      wakaba <wakabadou@gmail.com>
 * @license     http://opensource.org/licenses/MIT The MIT License MIT
 * @varsion     2.0.0
 */
class Arrays {
    /**
     * 配列に値がある場合は値を、無い場合はデフォルト値を返します。
     *
     * @param   mixed   $array          値の取得元配列
     * @param   mixed   $name           キー名
     * @param   mixed   $default_value  デフォルト値
     */
    public static function AdjustValue ($array, $name, $default_value = null) {
        if (is_array($array) || $array instanceof \Traversable) {
            foreach ((array) $name as $name) {
                if (isset($array[$name])) {
                    return $array[$name];
                }
            }
        }
        return $default_value;
    }

    /**
     * 渡された変数が配列の場合はそのまま配列として、配列以外の場合は配列にして返します。
     *
     * @param   mixed   $array  配列化したい変数
     * @param unknown_type $call_back
     * @return Ambigous <unknown, array, mixed>
     */
    public static function AdjustArray ($array, $call_back = null) {
        if ($call_back === null) {
            return is_array($array) ? $array : (array) $array;
        }
        return (is_array($array)) ? $array : call_user_func($call_back, $array);
    }

    /**
     * 変数がTraversableな値かどうか判定します。
     *
     * ※Traversableな値とはたとえばforeachで使えるものです。
     *
     * @param   Traversable $array  判定する値
     * @return  bool        Traversableな値の場合はbool TRUE、そうでない場合はbool FALSE
     */
    public static function IsTraversable ($array) {
        return is_array($array) || $array instanceof \Traversable;
    }

    /**
     * 配列のうち、指定したキーの要素のみを結合して返します。
     *
     * @param   string  $glue               連結値
     * @param   array   $array              対象の配列
     * @param   array   $target_key_list    結合対象とするキーのリスト
     * @return  string  結合された値
     */
    public static function SelectImplode ($glue, $array, $target_key_list) {
        $tmp = [];
        foreach ($target_key_list as $key) {
            $tmp[] = $array[$key];
        }
        return implode($glue, $tmp);
    }

    /**
     * 配列内の要素をbit値として合計します。
     *
     * @param   array   $bit_list   合計する配列
     * @return  float   合計された値
     */
    public static function BitSum ($bit_list) {
        if (!is_array($bit_list)) {
            return $bit_list;
        }

        $bit_sum = 0;
        foreach ($bit_list as $bit) {
            $bit_sum |= $bit;
        }

        return $bit_sum;
    }

    /**
     * 配列の中にキーが存在するか検証します。
     *
     * @param   array   $array  検索対象の配列
     * @param   mixed   $key    検索するキー
     * @return  bool    対象のキーが存在する場合はtrue、そうでない場合はfalse
     */
    public static function KeyExists ($array, $key) {
        return isset($array[$key]) || array_key_exists($key, $array);
    }

    /**
     * 指定した階層の配列にキーが存在するか検証します。
     *
     * @param   array   $array  検索対象の配列
     * @param   mixed   $key    検索するキー
     * @return  bool    対象のキーが存在する場合はtrue、そうでない場合はfalse
     */
    public static function ExistsLowest ($array, $keys) {
        foreach ((array) $keys as $key) {
            if (isset($array[$key])) {
                $array = $array[$key];
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * 指定された階層にある値でソートします。
     *
     * @param   array   $array  ソートする配列
     * @param   mixed   $name   階層
     * @return  array   ソートされた配列
     */
    public static function SortLowest ($array, $name) {
        uasort(
            $array,
            function ($current, $next) use ($name) {
                return strnatcmp(static::GetLowest($current, $name), static::GetLowest($next, $name));
            }
        );
        return $array;
    }

    /**
     * 指定された階層にある値で逆順ソートします。
     *
     * @param   array   $array  ソートする配列
     * @param   mixed   $name   階層
     * @return  array   ソートされた配列
     */
    public static function ReverseSortLowest ($array, $name) {
        uasort(
            $array,
            function ($current, $next) use ($name) {
                return -1 * strnatcmp(static::GetLowest($current, $name), static::GetLowest($next, $name));
            }
        );
        return $array;
    }

    /**
     * 指定された階層にある値を削除します。
     *
     * @param   array   $array  削除する値を持つ配列
     * @param   mixed   $keys   階層
     * @return  array   値を削除された配列
     */
    public static function RemoveLowest ($array, $keys) {
        $keys = (array) $keys;
        $target_key = array_pop($keys);
        $tmp =& $array;
        foreach ($keys as $key) {
            $tmp =& $tmp[$key];
        }
        unset($tmp[$target_key]);
        return $array;
    }

    /**
     * 指定された階層にある値を取得します。
     *
     * @param   array   $array  配列
     * @param   mixed   $keys   階層
     * @return  mixed   指定された改造にある値
     */
    public static function GetLowest ($array, $keys) {
        foreach ((array) $keys as $key) {
            if (isset($array[$key])) {
                $array = $array[$key];
            } else {
                return null;
            }
        }
        return $array;
    }

    /**
     * 指定された階層にある値を設定します。
     *
     * @param   array   $array  配列
     * @param   mixed   $keys   階層
     * @return  array   設定後の配列
     */
    public static function SetLowest ($array, $keys, $value) {
        $keys = (array) $keys;
        if (empty($array)) {
            $tmp =& $array;
        } else {
            $tmp =& $array[array_shift($keys)];
        }

        foreach ($keys as $key) {
            if ($tmp instanceof \ickx\fw2\vartype\arrays\LazyArrayObject) {
                if ($tmp->$key === null) {
                    $tmp->$key = null;
                }
                $tmp =& $tmp->$key;
            } else {
                if (!isset($tmp[$key])) {
                    $tmp[$key] = null;
                }
                $tmp =& $tmp[$key];
            }
        }
        $tmp = $value;
        return $array;
    }

    /**
     * 配列にあるキーの値を元に配列を階層化して返します。
     *
     * @param   array       $values 階層化する配列
     * @param   array       $keys   階層化の元にするキー名の配列
     * @param   callable    $filter 最終階層を詰める際に利用するコールバック
     * @return  array       階層化された配列
     */
    public static function DimensionalTransformation ($values, $keys = [], $filter = null) {
        if (is_array($keys) && empty($keys) || $keys === null) {
            return $values;
        }

        $keys = (array) $keys;
        $ret = [];
        foreach ($values as $row) {
            $tmp =& $ret;
            foreach ($keys as $key) {
                $tmp =& $tmp[$row[$key]];
            }
            if ($filter !== null && is_callable($filter)) {
                $row = $filter($row);
            }
            $tmp = $row;
        }
        return $ret;
    }

    /**
     * 配列から指定したキーに紐付く値のみを抽出し返します。
     *
     * 値が存在しない場合は値がnullとなります。
     *
     * @param array $array
     * @param array $keys
     */
    public static function GetElementsByKeys (array $array, array $keys) {
        return array_filter(array_combine($keys, array_map(function ($key) use ($array) {
            return isset($array[$key]) ? $array[$key] : null;
        }, $keys)));
    }

    /**
     * 再帰的に空要素を削除して配列を縮小します。
     *
     * @param   array       $array      空要素を削除する配列
     * @param   callable    $call_back  独自の空判定処理
     * @return  空要素を削除された配列
     */
    public static function RecursiveFilter ($array, $call_back = null) {
        if (!is_callable($call_back)) {
            foreach ($array as $idx => $element) {
                if (is_array($element)) {
                    $array[$idx] =  static::RecursiveFilter($element, $call_back);
                    if (empty($array[$idx])) {
                        unset($array[$idx]);
                    }
                }
                if ($element === null) {
                    unset($array[$idx]);
                }
            }
        } else {
            foreach ($array as $idx => $element) {
                if (is_array($element)) {
                    $array[$idx] =  static::RecursiveFilter($element, $call_back);
                    if (empty($array[$idx])) {
                        unset($array[$idx]);
                    }
                }
                if ($call_back($element, $idx)) {
                    unset($array[$idx]);
                }
            }
        }
        return $array;
    }

    /**
     * キーを維持したまま配列をシャッフルします。
     *
     * @param   array   $array  シャッフルする配列
     * @return  array   シャッフルされた配列
     */
    public static function Shuffle ($array) {
        $result = [];
        foreach(array_keys($array) as $key){
            $result[$key] = $array[$key];
        }
        return $result;
    }
}

PHP5.4が出た当初に作り直したモノなので、array_columnは使っていません。
悪しからず。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6