Manipulate massive array of strings - php

From one of our vendors I received a DVD with 12K+ images. Before i put them on our webserver, I need to resize, rename and copy them.
To do this I'm writing a PHP cli program.
And it seems that I am a little stuck with it...
All the files fit a certain pattern.
The copy and rename are not the problem, the manipulation of the strings is.
So to symplify the example code: lets suppose that I have an array with strings and I want to put them into a new array.
The original array looks like this:
$names = array (
'FIX1_VARA_000.1111_FIX2',
'FIX1_VARB_000.1111.2_FIX2',
'FIX1_VARB_222.2582_FIX2',
'FIX1_VARC_555.8794_FIX2',
'FIX1_VARD_111.0X00(2-5)_FIX2',
'FIX1_VARA_112.01XX(09-13)_FIX2',
'FIX1_VARB_444.XXX1(203-207).2_FIX2'
);
Each string in this array starts with the same fixed part in the front and ends with the same fixed part in the end FIX1 & FIX2 respectively.
After FIX1 there always is an underscore followed by a variable part, followed by an underscore. I'm not interested in the fixed parts or the variable parts. So I cut it all away.
The remaining string can be of the following two types:
If it only contains numbers and points: then it is a valid string and I put it
in the $clean array. EG: 000.1111 or 000.111.2
If the string does not only have numbers and points in it, then it always has several X's in it and a an open an closed bracked with numbers and a -.
Like 444.XXX1(203-207).2
The numbers between the brackets form a series, and each number in this series needs to replace the X's. The strings that should be put in the $clean array are:
444.2031.2
444.2041.2
444.2051.2
444.2061.2
444.2071.2
This is the part I'm struggling with.
$clean = array();
foreach ($names as $name){
$item = trim(strstr(str_replace(array('FIX1_', '_FIX2'),'',$name), '_'),'_');
// $item get the values:
/*
* 000.1111,
* 000.1111.2,
* 222.2582,
* 555.8794,
* 111.0X00(2-5),
* 112.01XX(09-13),
* 444.XXX1(203-207).2
*
*/
// IF an item has no X in it, it can be put in the $clean array
if (strpos($item,'X') === false){
//this is true for the first 4 array values in the example
$clean[] = $item;
}
else {
//this is for the last 3 array values in the example
$b = strpos($item,'(');
$e = strpos($item,')');
$sequence = substr($item,$b,$e-$b+1);
$item = str_replace($sequence,'',$item);
/* This is the part were I'm stuck */
/* ------------------------------- */
/* it should get the values in the sequence variable and iterate over them:
*
* So for $names[5] ('FIX1_VARA_112.01XX(09-13)_FIX2') I want the folowing values entered into the $clean array:
* Value of $sequence = '(09-13)'
*
* 112.0109
* 112.0110
* 112.0111
* 112.0112
* 112.0113
*
*/
}
}
//NOW ECHO ALL VALUES IN $clean:
foreach ($clean as $c){
echo $c . "\n";
}
The final output should be:
000.1111
000.1111.2
222.2582
555.8794
111.0200
111.0300
111.0400
111.0500
112.0109
112.0110
112.0111
112.0112
112.0113
444.2031.2
444.2041.2
444.2051.2
444.2061.2
444.2071.2
Any help with the "Here I'm stuck" part would be greatly appreciated.

Like #stdob-- mentioned, regular expressions really are what you want. Here's a working version of the code:
$names = array (
'FIX1_VARA_000.1111_FIX2',
'FIX1_VARB_000.1111.2_FIX2',
'FIX1_VARB_222.2582_FIX2',
'FIX1_VARC_555.8794_FIX2',
'FIX1_VARD_111.0X00(2-5)_FIX2',
'FIX1_VARA_112.01XX(09-13)_FIX2',
'FIX1_VARB_444.XXX1(203-207).2_FIX2'
);
$clean = array();
foreach ($names as $name){
$item = trim(strstr(str_replace(array('FIX1_', '_FIX2'),'',$name), '_'),'_');
// $item get the values:
/*
* 000.1111,
* 000.1111.2,
* 222.2582,
* 555.8794,
* 111.0X00(2-5),
* 112.01XX(09-13),
* 444.XXX1(203-207).2
*
*/
// IF an item has no X in it, it can be put in the $clean array
if (strpos($item,'X') === false){
//this is true for the first 4 array values in the example
$clean[] = $item;
}
else {
// Initialize the empty matches array (I prefer [] to array(), but pick your poison)
$matches = [];
// Check out: https://www.regex101.com/r/qG4jS4/1 to see visually how this works (also, regex101.com is just rad)
// This uses capture groups, which get stored in the $matches array.
preg_match('/\((\d*)-(\d*)\)/', $item, $matches);
// Now we've got the array of values that we want to have in our clean array
$range = range($matches[1], $matches[2]);
// Since preg_match has our parenthesis and digits grabbed for us, get rid of those from the string
$item = str_replace($matches[0],'',$item);
// Truly regrettable variable names, but you get the idea!
foreach($range as $number){
// Here's where it gets ugly. You're wanting the numbers to work like strings (have consistent length
// like 09 and 13) but also work like numbers (when you create a sequence of numbers). That kind of
// thinking begets hackery. This probably isn't your fault, but it seems helpful to point out.
// Anyways, we can use the number of X's in the string to figure out how many characters we ought
// to be adding. This is important because otherwise we'll end up with 112.019 instead of 112.0109.
// PHP casts that '09' to (int) 9 when we run the range() function, so we lose the leading zero.
$xCount = substr_count($item, 'X');
if($xCount > strlen($number)){
// This function adds a given number ($xCount, in our case) of a character ('0') to
// the end of a string (unless it's given the STR_PAD_LEFT flag, in which case it adds
// the padding to the left side)
$number = str_pad($number, $xCount, '0', STR_PAD_LEFT);
}
// With a quick cheat by padding an empty string with the same number of X's we counted earlier...
$xString = str_pad('', $xCount, 'X');
// Now we can add the fixed string into the clean array.
$clean[] = str_replace($xString, $number, $item);
}
}
}
// I also happen to prefer var_dump to echo, but again, your mileage may vary.
var_dump($clean);
It outputs:
array (size=18)
0 => string '000.1111' (length=8)
1 => string '000.1111.2' (length=10)
2 => string '222.2582' (length=8)
3 => string '555.8794' (length=8)
4 => string '111.0200' (length=8)
5 => string '111.0300' (length=8)
6 => string '111.0400' (length=8)
7 => string '111.0500' (length=8)
8 => string '112.0109' (length=8)
9 => string '112.0110' (length=8)
10 => string '112.0111' (length=8)
11 => string '112.0112' (length=8)
12 => string '112.0113' (length=8)
13 => string '444.2031.2' (length=10)
14 => string '444.2041.2' (length=10)
15 => string '444.2051.2' (length=10)
16 => string '444.2061.2' (length=10)
17 => string '444.2071.2' (length=10)
--Edit-- Removed my warning about strpos and ==, looks like somebody already pointed that out in the comments.

First, I'll suppose all your files have valid patterns, so no file has something wrong in it, otherwise, just add security conditions...
in $sequence, you get the (09-13).
To use digits, you have to remove ( and ), so make an other variable :
$range = substr($item,$b,$e-$b+1);
// you get '09-13'
then you need to split it :
list($min, $max) = explode("-",$range);
// $min = '09', $max = '13'
$nbDigits = strlen($max);
// $nbDigits = 2
Then you need all digits from min to max :
$numbersList = array();
$min = (int)$min; // $min becomes 9, instead of '09'
$max = (int)$max;
for($i=(int)$min; $i<=(int)$max; $i++) {
// set a number, including leading zeros
$numbersList[] = str_pad($i, $nbDigits, '0', STR_PAD_LEFT);
}
Then you have to generate file names with those digits :
$xPlace = strpos($item,'X');
foreach($numbersList as $number) {
$filename = $item;
for($i=0; $i<$nbDigits; $i++) {
// replacing one digit at a time, to replace each 'X'
$filename[$xPlace+$i] = $number[$i];
}
$clean[] = $filename;
}
It should do some work, there may be some errors, but it is a good start, give it a try :)

Related

Selecting multiple substrings from a string

I am a newbie here and I have a php array of some strings, such as {"VAL1","VAL2", "VAL3", "VAL4"} and I have a larger string (normally a text file). The text file contains the elements of the arrays at different positions and some or all may be repeating more than one. Each of of the elements of the array that are contained in the text file are immediately followed by a time of occurence, for example, "VAL1 0900UTC and other text information".The problem now is that I want to find the occurrence(s) of all of the elements of the array in the text file and the time value following the element.
Please note that some of the elements may occur more than once at different positions with different time value in the textfile.
Already, I can load the textfile and get all of the elements.:
$mytextfile = preg_replace('/\s+/', ' ', strtoupper(file_get_contents($textpath."/". $textfile)));
$substr = array("REALTK","BGPTK", "SUDTK", "BECTK");
$bigstring = "REALTK 1000UTC 16/14 1011 BGPTK 1030UTC 10/12 992 REALTK 1145UTC 00/14 2222 SUDTK 1412UTC 11/06 1011 REALTK 1600UTC 16/14 1015 ...";
//I created variables to hold all element occuring to false
$is_val1 = false; $is_val2 = false;$is_val3 = false; $is_val4 = false;
//I created variables to count how many of each substring exist in the string
$numofval1=$numofval2=$numofval3=$numofval4=0;
if(strpos($bigstring, $substr[0]) !== false) {
$is_val1 = true;
//if exist, count how many times
$numofval1 = substr_count(strtoupper($bigstring),$substr[0]);
} e.t.c
I have been able to get the occurrence of each of the array elements in the big string
I have been able to detect if any of the array elements occur more than once in big string and number of times it does.
But I have not being able to get them in the sequence they occur with their position in the textfile string and the time value after each element.
$mytextfile = preg_replace('/\s+/', ' ', strtoupper(file_get_contents($textpath."/". $textfile)));
$substr = array("REALTK","BGPTK", "SUDTK", "BECTK");
//this is a sample content of the textfile
$bigstring = "REALTK 1000UTC 16/14 1011 BGPTK 1030UTC 10/12 992 REALTK 1145UTC 00/14 2222 SUDTK 1412UTC 11/06 1011 REALTK 1600UTC 16/14 1015 ...";
//I created variables to hold all element occuring to false
$is_realtk = false; $is_bgptk = false;$is_sudtk = false; $is_bectk = false;
//I created variables to count how many of each of the element exist in the text file string
$numofrealtk=$numofbgptk=$numofsudtk=$numofbectk=0;
if(strpos($bigstring, $substr[0]) !== false) {
$is_realtk = true;
//if exist, count how many times
$numofrealtk = substr_count(strtoupper($bigstring),$substr[0]);
} e.t.c
What I need is to get the Elements of the array in the order in which they occur in the text file with their position and time value
REALTK POSITION1 1000UTC
BGPTK POSITION5 1030UTC
REALTK POSITION8 1145UTC
SUDTK POSITION13 1412UTC
REALTK POSITION17 1600UTC
I also want to store the element => timevalue as associative array.
Thanks in anticipation.
Here is the snippet for you, please see inline doc for explanation
$substr = array("VAL1","VAL2", "VAL3", "VAL4");
$bigstring = "OTHERS VAL1 VAL4 VAL1 OTHERS OTHERS VAL2 OTHERS";
// considering space to explode words and having numbers in it
$temp = array_unique(str_word_count($bigstring, 1,"0..9"));
// sorting generated array and keeping index of value
asort($temp);
// matching records from array
$temp = array_intersect($temp,$substr);
// showing all the data you want
array_walk($temp,function($item,$k){
echo $item.' POSITION '.$k."\n"; // replace \n with <br/> if web
});
Working demo.
If the elements in your $bigstring are always divided by a whitespace, you can explode:
$val_arr = array( "REALTK" => array(), "BGPTK" => array(), "SUDTK" => array(), "BECTK" => array();
$bigstring_arr = explode( " ", $bigstring);
foreach( $bigstring_arr as $key => $part ){
if( in_array( $part, $substr ) ){
$val_arr[$part][] = array( "position" => $key+1, "time" => $bigstring_arr[$key+1] );
}
}
If it's not you can also explode directly on each element of Val1, Val2... . The code gets a bit more complicated then. But it would work as well if you created a new array in the form of "CountOfAllCharsInBigstringBeforeOccurence" => "KeyOfValInSubstr". From that you can derive your ordering with the vals.
Edit:
I edited the code based on your comment. Please note that it is not possible to have an array that has the same key multiple times which is why the solution here will have multiple dimensions.

How to store strings into an array from a foreach loop?

To start off, I've read the handful of questions here asking similar questions but nothing that I could take example of and implement properly into my code. I've got an array that hold a few thousand numbers, they're the names of a file in a directory. I need to run a MySQL Query that searches for those file names, but I need all the individual queries to be in another array.
Here's my code to grab the filenames:
$dir = "C:/Users/EyeCandy-Brian/Desktop/jobs";
$files = array_diff(scandir($dir), array('..', '.'));
function truncate(&$fcn) {
$fcn = substr($fcn, 0, 6); // The original is '100100.dat', I only need the number
};
array_walk($files, "truncate");
The array looks like this:
2 => string '100100'
3 => string '100101'
4 => string '100102'
5 => string '100103'
// About 1,500+ Total
And here's my foreach loop with my declared variables:
$limit = 0; // This is for testing, stops after 5 iterations.
$qryArrNum = 2; // The index for the $files array, [0] & [1] were '.' & '..'
$count = 0; // Which index I want to store the string in, starting at [0]
$qry = array(); // I declare $qry as an array
foreach ($files as &$qry) {
$qry[$count++] = "SELECT * FROM `table` WHERE `sku` LIKE '%$files[$qryArrNum]%'";
++$qryArrNum;
if (++$limit == 5) {
break;
};
};
Here's where it gets wild. When using var_dump($qry); I get '1001S4' which shouldn't be. Running var_dump($files); The first five strings of the array are:
2 => string 'S00100'
3 => string '1S0101'
4 => string '10S102'
5 => string '100S03'
6 => string '1001S4'
Then following that are the expected strings from the filenames:
7 => string '100105'
8 => string '100106'
9 => string '100107'
// On for 1,500+ again
So my question is, how do I get $qry to be an array that contains a string, each string being the MySQL Query containing a search for the first value from $files, and the next string in the $qry array will search for the next value from $files. Also, why are the first handful values in $files completely different than what's expected?
NOTE: I understand that running a thousand plus MySQL Queries sequentially will murder my server, I'll use implode(); to combine them into one large query, I just need to figure this out initially.
Instead of running multiple queries you could use the mysql find_in_set().
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_find-in-set
Once you have your files, use implode to convert to a string
$tmp = implode(",",$files);
Your sql statement would be:
$sql = "SELECT * FROM `table` WHERE FIND_IN_SET(`sku`,'{$tmp}')>0";
This will return all the matching items

How to create and fill a new associative array with values created in a for each?

I am a noob attempting to solve a program for a word search app I am doing. My goal is to take a string and compare how many times each letter of that string appears in another string. Then put that information into an array of key value pairs, where the key is each letter of the first string and the value is the number of times. Then order it with ar(sort) and finally echo out the value of the letter that appears the most (so the key with the highest value).
So it would be something like array('t' => 4, 'k' => '9', 'n' => 55), echo the value of 'n'. Thank you.
This is what I have so far that is incomplete.
<?php
$i = array();
$testString= "endlessstringofletters";
$testStringArray = str_split($testString);
$longerTestString= "alphabetalphabbebeetalp
habetalphabetbealphhabeabetalphabetalphabetalphbebe
abetalphabetalphabetbetabetalphabebetalphabetalphab
etalphtalptalphabetalphabetalbephabetalphabetbetetalphabet";
foreach ($testStringArray AS $test) {
$value = substr_count($longerTestString, $testStringArray );
/* Instead of the results of this echo, I want each $value to be matched with each member of the $testStringArray and stored in an array. */
echo $test. $value;
}
/* I tried something like this outside of the foreach and it didn't work as intended */
$i = array_combine($testStringArray , $value);
print_r($i);
If I understand correctly what you are after, then it's as simple as this:
<?php
$shorterString= "abc";
$longerString= "abccbaabaaacccb";
// Split the short sring into an array of its charachters
$stringCharachters = str_split($shorterString);
// Array to hold the results
$resultsArray = array();
// Loop through every charachter and get their number of occurences
foreach ($stringCharachters as $charachter) {
$resultsArray[$charachter] = substr_count($longerString,$charachter);
}
print_r($resultsArray);

Grouping in PHP using a character

I have an array like:
array{
0 => string 'B.E - ECE',
1 => string 'B.E - EEE',
2 => string 'Msc - Maths',
3 => string 'Msc - Social',
}
So how can I make the array into groups like:
B.E. => ECE, EEE
Msc => Maths,Social
?
I want to do it in PHP. Can anybody help me how to achieve it ?
So is your array split by the "-" character?
so it's Key - Value pairs split by commas?
Ok -
(edit: section removed to clarify answer)
Following conversation and some rearrangement of the question, a second try at a solution, with the above assumptions, try this:
$array = array {
0 => string 'B.E - ECE' (length=9)
1 => string 'B.E - EEE' (length=9)
2 => string 'Msc - Maths' (length=11)
3 => string 'Msc - Social' (length=12)
}
foreach ($array as $row){
$piece = explode("-",$row);
$key = $piece[0];
$newArray[$key][] = $piece[1];
unset($piece);
}
unset($row) ///tidy up
This will output two arrays each of two arrays:
$newArray[Msc] = array("Maths","Social");
$newArray[B.E] = array("ECE","EEE");
What I did was cause the Foreach loop to automatically add onto the array if the key exists with $newArray[$key][] so that the values are automatically collected by key, and the key is defined as the first half of the original array values.
Printing:
To print the result:
foreach($newArray as $key=>$newRow){
/// there are two rows in this case, [B.E] and [MSc]
print $key.":<br>";
print "<pre>";
///<pre> HTML tag makes output use linebreaks and spaces. neater.
print_r($newRow);
///alternatively use var_dump($newRow);
print "</pre>";
}
Alternatively if you wish to print a known named variable you can write:
print_r($newArray['B.E']);
Which will print all the data in that array. print_r is very useful.
what you want is php's explode. Not sure if this will give you the perfect answer but should give you an idea of what to do next.
$groupedArray = array();
foreach($array as $row){
$split = explode(" - ",$row);
$groupedArray[] = $split[0];
}
array_unique($groupedArray); //This will give you the two groupings
foreach($array as $row){
$split = explode(" - ",$row);
$pos = array_search($split[0],$groupedArray);
if($pos !== FALSE){
$groupedArray[$pos][] = $split[1];
}
}
This should give you a full formatted array called $groupedArray where $array is the array you already have.
Hope this helps!

Creating menu from Text

I am very new to PHP programming and dont know the exact syntax of various labrary functions like , split, find ,etc.
I am having following text with me
in a string variable
$menu = '/home-page|HOME
/our-iphone-app|OUR iPhone APP
/join-us|JOIN ME
/contact-us|CONTACT US';
I want to populate to arrays with this text one array containing the portion before the pipe | and second array contains portions after pipe. How to do this using some split by char(|) method.
Finally arrays must contain
$arraypage = {'0'->'/home-page','1'->'/our-iphone-app'} // etc, and...
$arrayTitle = {'0'->'HOME','2'->'OUR iPhone App'} // etc
You need to break up the string by new lines and then by pipe characters.
$lines = explode("\n", $menu);
$arraypage = array();
$arrayTitle = array();
foreach($lines as $line) {
list($arraypage[], $arrayTitle[]) = explode('|', $line);
}
var_dump of the resulting arrays gives:
array
0 => string '/home-page' (length=10)
1 => string '/our-iphone-app' (length=15)
2 => string '/join-us' (length=8)
3 => string '/contact-us' (length=11)
array
0 => string 'HOME' (length=4)
1 => string 'OUR iPhone APP' (length=14)
2 => string 'JOIN ME' (length=7)
3 => string 'CONTACT US' (length=10)
$array = explode("\n", $array);
$result1 = array();
$resutl2 = array();
foreach($array as $arr){
$temp = explode('|', $arr);
$result1[] = $temp[0];
$result2[] = $temp[1];
}
I'd suggest you make your string contain another separator like so:
$menu = '/home-page|HOME:/our-iphone-app|OUR iPhone APP:/join-us|JOIN ME:/contact-us|CONTACT US';
Then you can use the explode method to split up your string to an associative array.
$array = explode(":", $menu);
foreach($array as $key => $val) {
$array[$key] = explode("|", $val);
}
I'd probably just define the data in an array to start with, rather than as a string. I'd also build it in the format
$menu_array = {
{'url'=>'/home-page','text'=>'HOME'},
{'url'=>'/our-iphone-app','text'=>'OUR iPhone APP'},
{'url'=>'/join-us','text'=>'JOIN ME'},
{'url'=>'/contact-us','text'=>'CONTACT US'},
};
Since that's almost certainly going to be more useful for whatever you do with it next.
If you do need to use a string for some reason though, I'd say a regular expression is the tidiest way to do this. The following:
preg_match_all('/^(?P<url>[^|]+)\\|(?P<text>\\V+)\\v*$/m',$menu,$menu_array,PREG_SET_ORDER)
would set $menu_array to the format I used above.
In fairness, using a regular expression for this may be a little overkill, but I prefer the power of regexes, which are easier to tweak later when you want to add things than loops of explode()s.

Categories