Need some help making a php search tree - php

Im making a tree to store words and an associated number array in php. I need it to look something like this:
Words: apple, ant
[a] => Array
(
[p] => Array
(
[p] => Array
(
[l] => Array
(
[e] => Array
(
[0] => Array
(
[0] => 0
[1] => 0
[2] => 1
[3] => 2
[4] => 3
[5] => 4
)
)
)
)
)
[n] => Array
(
[t] => Array
(
[0] => Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 0
[4] => 0
[5] => 4
)
)
)
)
Of course apple and ant need to share the same [a] index. Im close, but I cant figure out how to properly keep track of the tree index so 'apple' gets into the tree fine but 'ant' is inserted as 'nt'. Heres my code at the moment:
private function insertWordsIntoTree()
{
foreach ($this->words as $word)
{
$characters = preg_replace('/[0-9]+/', '', $words);
$points = $this->getPoints($word);
$this->tree = $this->buildTree($characters, $points, $this->tree);
}
print_r($this->tree);
}
private function buildTree($characters, array $points, array $tree)
{
for ($i = 0; $i < strlen($characters); $i++)
{
$character = $characters[$i];
$remaining_characters = substr($characters, $i + 1);
if (strlen($characters) === 1)
{
$child = [];
$child[$character] = [$points];
return $child;
}
elseif (!isset($tree[$character]))
{
$tree[$character] = $this->buildTree($remaining_characters, $points, []);;
break;
}
else
{
$this->buildTree($remaining_characters, $points, $tree[$character]);
}
}
return $tree;
}
Im pretty sure the problem is at the else statement...I dont think Im keeping track of the current tree index properly. Any help would be much appreciated.

Here's a simple approach that passes the recursion off to php:
$tree = array();
foreach($words as $word) {
$characters = array_reverse(str_split($word));
$temp = array();
foreach($characters as $index => $character) {
if($index == 0) {
$temp[] = getPoints($word);
}
$temp = array(
$character => $temp
);
}
$tree = array_merge_recursive($tree, $temp);
}

Related

convert string to multidimensional array

I have this array
dev3->content->->mktg->->->pls1->->->pls2->->->config->->splash
I want to convert this string to multidimensional array. like this
Array
(
[0] => dev3
Array (
[0] => ->content
Array (
[0] => ->->mktg
Array(
[0] => ->->->pls1
[1] => ->->->pls2
[2] => ->->->config
)
[1] => ->->splash
)
)
)
Can anyone do this
it does not work if level will be increaed more then +1 on any step
$str = 'dev3->content->->mktg->->->pls1->->->pls2->->->config->->splash';
$in = preg_split('/(?<!>)(?=->)/', $str);
Above we make such array from the input string
Array
(
[0] => dev3
[1] => ->content
[2] => ->->mktg
[3] => ->->->pls1
[4] => ->->->pls2
[5] => ->->->config
[6] => ->->splash
)
continue working
$result = [];
$p = &$result;
$level = 0;
foreach($in as $i) {
// Count next level
$c = substr_count($i, '->');
// if level is not changed
if($c == $level) { $p[] = $i; continue; }
// level increased
if ($c == $level + 1) {
$level++;
$p[] = [$i];
$p = &$p[count($p)-1];
continue;
}
// any level less then achived before
if ($c < $level) {
$p = &$result;
$level = $c;
while($c--)
$p = &$p[count($p)-1];
$p[] = $i;
continue;
}
die("I can't process this input string");
}
print_r($result);
working demo

Associative index array to associative associative array

Problem
I have an array which is returned from PHPExcel via the following
<?php
require_once 'PHPExcel/Classes/PHPExcel/IOFactory.php';
$excelFile = "excel/1240.xlsx";
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objPHPExcel = $objReader->load($excelFile);
foreach ($objPHPExcel->getWorksheetIterator() as $worksheet) {
$arrayData[$worksheet->getTitle()] = $worksheet->toArray();
}
print_r($arrayData);
?>
This returns:
Array
(
[Films] => Array
(
[0] => Array
(
[0] => Name
[1] => Rating
)
[1] => Array
(
[0] => Shawshank Redemption
[1] => 39
)
[2] => Array
(
[0] => A Clockwork Orange
[1] => 39
)
)
[Games] => Array
(
[0] => Array
(
[0] => Name
[1] => Rating
)
[1] => Array
(
[0] => F.E.A.R
[1] => 4
)
[2] => Array
(
[0] => World of Warcraft
[1] => 6
)
)
)
What I would like to have is
Array
(
[Films] => Array
(
[0] => Array
(
[Name] => Shawshank Redemption
[Rating] => 39
)
[1] => Array
(
[Name] => A Clockwork Orange
[Rating] => 39
)
)
[Games] => Array
(
[0] => Array
(
[Name] => F.E.A.R
[Rating] => 4
)
[1] => Array
(
[Name] => World of Warcraft
[Rating] => 6
)
)
)
The arrays names (Films, Games) are taken from the sheet name so the amount can be variable. The first sub-array will always contain the key names e.g. Films[0] and Games[0] and the amount of these can be varible. I (think I) know I will need to do something like below but I'm at a loss.
foreach ($arrayData as $value) {
foreach ($value as $rowKey => $rowValue) {
for ($i=0; $i <count($value) ; $i++) {
# code to add NAME[n] as keys
}
}
}
I have searched extensively here and else where if it is a duplicate I will remove it.
Thanks for any input
Try
$result= array();
foreach($arr as $key=>$value){
$keys = array_slice($value,0,1);
$values = array_slice($value,1);
foreach($values as $val){
$result[$key][] = array_combine($keys[0],$val);
}
}
See demo here
You may use nested array_map calls. Somehow like this:
$result = array_map(
function ($subarr) {
$names = array_shift($subarr);
return array_map(
function ($el) use ($names) {
return array_combine($names, $el);
},
$subarr
);
},
$array
);
Demo
Something like this should work:
$newArray = array();
foreach ($arrayData as $section => $list) {
$newArray[$section] = array();
$count = count($list);
for ($x = 1; $x < $count; $x++) {
$newArray[$section][] = array_combine($list[0], $list[$x]);
}
}
unset($arrayData, $section, $x);
Demo: http://ideone.com/ZmnFMM
Probably a little late answer, but it looks more like your tried solution
//Films,Games // Row Data
foreach ($arrayData as $type => $value)
{
$key1 = $value[0][0]; // Get the Name Key
$key2 = $value[0][1]; // Get the Rating Key
$count = count($value) - 1;
for ($i = 0; $i < $count; $i++)
{
/* Get the values from the i+1 row and put it in the ith row, with a set key */
$arrayData[$type][$i] = array(
$key1 => $value[$i + 1][0],
$key2 => $value[$i + 1][1],
);
}
unset($arrayData[$type][$count]); // Unset the last row since this will be repeated data
}
I think this will do:
foreach($arrayData as $key => $array){
for($i=0; $i<count($array[0]); $i++){
$indexes[$i]=$array[0][$i];
}
for($i=1; $i<count($array); $i++){
for($j=0; $j<count($array[$i]); $j++){
$temp_array[$indexes[$j]]=$array[$i][$j];
}
$new_array[$key][]=$temp_array;
}
}
print_r($new_array);
EDIT: tested and updated the code, works...

PHP Sort Mega-Array by Value

I am looking for some advices how could I sort this kind of Array by 'variant_name' key.
Because Array is really huge I minified it to the looking result state.
...
$filter[2413][1][81][sub_id] = 1;
$filter[2413][1][81][variant_id] = 81;
$filter[2413][1][81][variant_name] = 'Banana';
$filter[2413][2][87][sub_id] = 2;
$filter[2413][2][87][variant_id] = 87;
$filter[2413][2][87][variant_name] = 'Apple';
$filter[2413][3][32][sub_id] = 3;
$filter[2413][3][32][variant_id] = 32;
$filter[2413][3][32][variant_name] = 'Carrot';
...
Keys $filter[x][x][x] are not sequential.
I have tried the sort function I used before but it doesn't work with this kind of Array:
function array_sort_by_column(&$arr, $col, $dir = SORT_ASC) {
$sort_col = array();
foreach ($arr as $key=> $row) {
$sort_col[$key] = $row[$col];
}
array_multisort($sort_col, $dir, $arr);
}
array_sort_by_column($filter[][][], 'variant_name');
My target is modify array by sorting 'variant_name' to 'Apple', 'Banana', 'Carrot' accordingly keeping the array structure.
It's a working code tested from given examples.
Note that my codes still can be optimized, etc. Just take it as my advice.
TL;DR = Use usort().
function mySort($a,$b)
{
$av = "";
$bv = "";
foreach($a as $ak)
$av = $ak['variant_name'];
foreach($b as $bk)
$bv = $bk['variant_name'];
if($av[0] < $bv[0])
return false;
else return true;
}
How to use it? You have to specify which first level array to sort.
usort($filter['2413'],"mySort");
Then the result I got is:
Array
(
[2413] => Array
(
[0] => Array
(
[87] => Array
(
[sub_id] => 2
[variant_id] => 87
[variant_name] => Apple
)
)
[1] => Array
(
[81] => Array
(
[sub_id] => 1
[variant_id] => 81
[variant_name] => Banana
)
)
[2] => Array
(
[32] => Array
(
[sub_id] => 3
[variant_id] => 32
[variant_name] => Carrot
)
)
)
)

Merging array in recursive function using php?

After one week searching and converting many algorithm from other language into php to make an array that contain "combination k from n". I'm stuck.
please help me.
This is my code (using php):
function comb($item,$arr,$out, $start, $n, $k, $maxk) {
if ($k > $maxk) {
foreach($arr as $ar){
echo "$ar";
echo "<br/>";
}
return;
}
for ($i=$start; $i<=$n; $i++) {
$arr[$k] = $item[$i-1];
comb($isi, $arr, $out, $i+1, $n, $k+1, $maxk);
}
}
$team = array("A","B","C","D");
$ar = array();
$o = array();
comb($team,$ar,$o,1,4,1,2);
Recursive algorithm above is really confuse me. The code above was successful to form the combination but I cannot merge them into one array because of its recursive characteristics. I just want to make an array that contain the result of combination of 2 from 4 items. Like this (see below)
Array (
[0] => Array (
[1] => A
[2] => B
)
[1] => Array (
[1] => A
[2] => C
)
[2] => Array (
[1] => A
[2] => D
)
[3] => Array (
[1] => B
[2] => C
)
[4] => Array (
[1] => B
[2] => D
)
[5] => Array (
[1] => C
[2] => D
)
)
I know I still far from the answer. But please guide me, to reach that answer. Perhaps you know the other technique, it doesn't matter. If your code works, I will use it. No matter what the technique you've used. Any ideas would be gratefully appreciated,Thank you..!
An (I think) simpler recursive implementation is:
<?php
/* Given the array $A, returns the array of $k-subsets
of $A in lexicographical order. */
function k_lex_subset($A,$k) {
if (($k <= 0) or (count($A) < $k)) { return array(); }
else if ($k <= 1) { return array_chunk($A,1); }
else {
$v = array_shift($A);
$AwA = k_lex_subset($A,$k-1);
foreach($AwA as &$vp) {
array_unshift($vp,$v);
}
$AwoA = k_lex_subset($A,$k);
$resultArrs = array_merge($AwA, $AwoA);
return($resultArrs);
}
}
$team = array("A","B","C","D");
print_r(k_lex_subset($team,2));
?>
which returns
Array
(
[0] => Array
(
[0] => A
[1] => B
)
[1] => Array
(
[0] => A
[1] => C
)
[2] => Array
(
[0] => A
[1] => D
)
[3] => Array
(
[0] => B
[1] => C
)
[4] => Array
(
[0] => B
[1] => D
)
[5] => Array
(
[0] => C
[1] => D
)
)
and will work for any size array, and any $k.
The term you are looking for is (lexicographical) k-subset enumeration where $k is 2 in this specific case.
Explanation
The idea is very simple. Assume we have (for example) a set {A,B,C,D}. We want to start with all sets with A in, and so we consider subsets of size 1-less coming from {B,C,D} and append A to them yielding
{{A,B}, {A,C}, {A,D}}
and then we consider all subsets of size 2 without A in
{{B,C}, {B,D}, {C,D}}
and then we just merge the two. It is hopefully easy to see how, in general, this yields a nice recursive strategy for constructing the k-subsets of a set (instead of just k=2).
Reference
A fantastic reference on this sort of thing is Vol 4 Fasicle 3 of Knuth's The Art of Computing Programming.
This should do the trick to reach the array you've described above:
<?php
$array = array("A","B","C","D");
function transformArray( $array ) {
$returnArray = array();
for( $i=0; $i < count($array); $i++ ) {
for( $j=$i+1; $j < count($array); $j++ ) {
$returnArray[] = array( $array[$i], $array[$j] );
}
}
return $returnArray;
}
print_r(transformArray($array));
?>
Not exactly sure on the necessity of start, n, and k, but this should get you the expected output. If you provide some more details on why those counters would be necessary, we can get you a more thorough answer.
function comb($itemArray, $start, $n, $k, $maxk) {
//if ($k > $maxk) return;
$outputArray = array();
foreach($itemArray AS $index => $firstChar) {
for($i = $index+1; $i<count($itemArray); $i++) {
$secondChar = $itemArray[$i];
$outputArray[] = array($firstChar, $secondChar);
}
}
return $outputArray;
}
$teamArray = array("A","B","C","D");
$resultArray = comb($teamArray,1,4,1,2);
ppr($resultArray);
function ppr($variable) {
echo '<pre>';
print_r($variable);
echo '</pre>';
}
function comb($a, $len){
if ($len > count($a))return array();
$out = array();
if ($len==1) {
foreach ($a as $v) $out[] = array($v);
return $out;
}
$len--;
while (count($a) > $len) {
$b = array_shift($a);
$c = comb($a, $len);
foreach ($c as $v){
array_unshift($v, $b);
$out[] = $v;
}
}
return $out;
}
$test = array('a','b','c','d');
$a = comb($test,2);
print_r($a);
would give you:
Array(
[0] => Array
(
[0] => a
[1] => b
)
[1] => Array
(
[0] => a
[1] => c
)
[2] => Array
(
[0] => a
[1] => d
)
[3] => Array
(
[0] => b
[1] => c
)
[4] => Array
(
[0] => b
[1] => d
)
[5] => Array
(
[0] => c
[1] => d
)
)
Why are you doing
echo "ar"
instead of
echo $ar
Also are you looking for permutation or combination? Here is a site with very nice algorithms for both. The implementation is in java but it's very clear. so converting to php won't be difficult: http://geekviewpoint.com/Numbers_functions_in_java/
If you want a pure clean recursive way use this:
function comb($item, $n) {
return comb_rec($item, array(), $n);
}
function comb_rec($items, $c, $n) {
if (count($c) == $n) {
return array($c);
}else{
if (count($items) == 0) {
return $items;
}else{
$list = $items;
$head = array_shift($list);
$tail = $list;
$current = $c;
array_push($current,$head);
return array_merge(comb_rec($tail, $current, $n),comb_rec($tail, $c, $n));
}
}
}
$team = array("A","B","C","D");
$all = comb($team,2);
print_r($all);

Anyway to simplify this rats nest of foreach loops?

This works but is uglier than hell, basically it's iterating through two separate portions of a sub array, seeing if there's a greatest common denominator besides 1 in the values of both sub arrays, and if there is, multiplying the base value by 1.5
Sorry for the sloppy code ahead of time.
error_reporting(E_ALL);
ini_set('display_errors', '1');
class CSVParser
{
public $output = NULL;
public $digits = NULL;
public function __construct($file)
{
if (!file_exists($file)) {
throw new Exception("$file does not exist");
}
$this->contents = file_get_contents($file);
$this->output = array();
$this->digits = array();
$this->factor = array();
}
public function parse($separatorChar1 = ',', $separatorChar2 = ';', $enclosureChar = '"', $newlineChar = "\n")
{
$lines = explode($newlineChar, $this->contents);
foreach ($lines as $line) {
if (strlen($line) == 0) continue;
$group = array();
list($part1, $part2) = explode($separatorChar2, $line);
$group[] = array_map(array($this, "trim_value"), explode($separatorChar1, $part1), array("$enclosureChar \t"));
$group[] = array_map(array($this, "trim_value"), explode($separatorChar1, $part2), array("$enclosureChar \t"));
$this->output[] = $group;
}
}
private function trim_value($value, $chars)
{
return preg_replace("#^( |" . $chars . ")+#", '', $value);
}
private function gcd($x,$y)
{
do {
$rest=$x%$y;
$x=$y;
$y=$rest;
} while($rest!==0);
return $x;
}
public function algorithm()
{
$alpha = array(
'c' => str_split('bcdfghjklmnpqrstvwxz'),
'v' => str_split('aeiouy')
);
$i=$k=0;
foreach ($this->output as $item) {
$cnt = 0;
$this->digits[$i] = array();
foreach ($item as $part) {
$this->digits[$i][$cnt] = array();
$new = array();
foreach ($part as $str) {
$v = count(array_intersect(str_split($str), $alpha['v']));
$c = count(array_intersect(str_split($str), $alpha['c']));
$t = strlen(str_replace(' ', '', $str));
$new = ($cnt == 0)
? array('v' => $v, 'c' => $c, 't' => $t, 'm' => ($t%2) ? $v * 1.5 : $c)
: array('v' => $v, 'c' => $c, 't' => $t);
$this->digits[$i][$cnt][] = $new;
}
$cnt++;
}
$i++;
}
$h=$cuml=0;
foreach($this->digits as &$slice) {
foreach($slice[0] as &$sliceName){
foreach($slice[1] as $sliceProduct) {
foreach($sliceProduct as $pKey=>$pVal) {
foreach($sliceName as $nKey=>$nVal) {
$tmp[$h] = ($this->gcd($pVal,$nVal) != 1) ? ++$cuml:'';
}
}
$tmp[$h] = $sliceName['m']*$cuml*1.5;
$h++;
$cuml=0;
}$h=0;
$sliceName['f'] = $tmp;
$tmp='';
}
}
foreach($this->digits as &$u){unset($u[1]);}
}
}
$parser = new CSVParser("file.csv");
$parser->parse(); //print_r($parser->output);
$parser->algorithm(); print_r($parser->digits);
Sample CSV per request
Jeff Goes, Mika Enrar;Triple Threat, Dogs on Bikes
Sonny Ray, Lars McGarvitch, Jason McKinley;Kasabian, Lords of Acid, Hard-Fi
The Output
Array
(
[0] => Array
(
[0] => Array
(
[0] => Array
(
[v] => 3
[c] => 3
[t] => 8
[m] => 3
[f] => Array
(
[0] => 40.5
[1] => 4.5 // Remainder.. So 'Jeff Goes' => 'Dogs on Bikes'
)
)
[1] => Array
(
[v] => 3
[c] => 4
[t] => 9
[m] => 4.5
[f] => Array
(
[0] => 67.5 // High Score! So 'Mika Enrar' => 'Triple Threat'
[1] => 13.5
)
)
)
)
[1] => Array
(
[0] => Array
(
[0] => Array
(
[v] => 4
[c] => 2
[t] => 8
[m] => 2
[f] => Array
(
[0] => 24
[1] => 12
[2] => 24 // Next Highest 'Sonny Ray' => 'Hard-Fi'
)
)
[1] => Array
(
[v] => 3
[c] => 8
[t] => 14
[m] => 8
[f] => Array
(
[0] => 84 // High Score! (This is really a tie, but 'm' has the highest secondary value so...)
[1] => 60 // 'Lars McGarvitch => 'Kasabian'
[2] => 84
)
)
[2] => Array
(
[v] => 5
[c] => 5
[t] => 13
[m] => 7.5
[f] => Array
(
[0] => 0
[1] => 0 // The only one left 'Jason McKinley' => 'Lords of Acid'
[2] => 11.25
)
)
)
)
)
What it does
What this class does so far is split the csv one array, split content prior to ; and after into two sub arrays. Count the consonants and vowels of both, find if there is a greatest common denominator between the two subsections for each C V or mixed letter pair, and create a value to assign a band to a product.
What really needs to do though
The highest value generated should be associated with the band that created that high value. So what I am trying to really do is associate a name to a band depending on how high of a score it ultimately generates. I'm about half way through =(
As you guys can see, this code is a mess, literally. All I really want is to assign a name to a band based on the numbers I'm generating.
I have to agree with everyone else here... but I'd like to add:
Instead searching for how to traverse $this->digits more simply, you should strongly consider rethinking the structure of the data in $this->digits.
Furthermore, lumping everything into a single array doesn't always make sense. But when it does, the structure can be thought out so that it is intuitive and can be traversed easily.
Without more information about what this is doing, there is no way for us to suggest how to restructure your data / class. A start would be giving us what a sample $this->digits array looks like. Also, some more information about your problem would be good (like how this method is used).
If it works why are you changing it? Performance? Refactor? Business changed? Requirements changed? Clean code Samaritan? Boy Scout rule?
When I come across "spaghetti code" I leave it alone unless I absolutely must change it. That said, I would write a couple of unit tests verifying the output of the "spaghetti code" so that I know that I did not break anything or make things worse.

Categories