What: I'm attempting to compare data in two arrays and write a statement based on the comparison,
$sys = array("1"=>'kitchen lights', "2"=>'living lights', "3"=>'living fan');
$input = array('off kitchen lights','on living fan');
Note: The input can come in any order! :-/ any ideas
Compare these to allow for me to change the state in a database and write to a change log.
The sys array key is also important here.
Im shooting for a result like the following:
$write = '1:0,2:256';// means off kitchen lights and on living fan
The write is broken into bits like this:
($sys array key number):('256' on or off '0'),(separate next listing...)
Im familiar with array_intersect.
$wordin = explode(" ", $input);
$wordsys = explode(" ", $sys);
$result = array_intersect($wordin, $wordsys);
Im sure I could loop through the array looking for lets say on and replace it with 256 or 0 but im running to issues thinking of how to do the following:
Handle variations like lights versus light...I need them to be equal for this...
Preserve the sys array key number
Note: Im not sure of a "easier" method but I will take any feed back!
Thanks,
JT
More Info: A user types a string. Im pulling all the detail out of the string and arriving at the input array. The sys is a predefined database that the user set up.
To have different triggers for the same thing, you can do something like this (allows you to add more triggers easily). You could also place some regex in the triggers and evaluate them, but you can figure that out yourself ;)
<?php
define('SWITCHED_ON', 256);
define('SWITCHED_OFF', 0);
$sys = array(
'1' => array(
'name' => 'Kitchen Lights',
'triggers' => array(
'kitchen light',
'kitchen lights',
),
),
'2' => array(
'name' => 'Living Lights',
'triggers' => array(
'living lights',
'lights in living room',
'light in living room',
),
),
'3' => array(
'name' => 'Living Fan',
'triggers' => array(
'living fan',
'fan in living room',
),
),
);
$input = array('off kitchen lights','on living fan');
$output = array();
foreach ( $input as $command ) {
// split command at first whitespace
// $command_array = preg_split('%\s+%', $command, 2);
// update to allow input like $input = array('kitchen off lights','living fan on');
$split = preg_split('%\s+%', $command);
$input_switch = false;
$input_trigger = array();
foreach ( $split as $part ) {
if ( $input_switch === false ) {
switch ( $part ) {
case 'on': $input_switch = SWITCHED_ON; break;
case 'off': $input_switch = SWITCHED_OFF; break;
default: $input_trigger[] = $part; break;
}
} else {
$input_trigger[] = $part;
}
}
if ( $input_switch === false || empty($input_trigger) ) {
continue;
}
$input_trigger = implode(' ', $input_trigger);
// insert check if command is valid (for example contains only spaces and alphanumerics.. etc..)
// ...
foreach ( $sys as $syskey => $conf ) {
foreach ( $conf['triggers'] as $trigger ) {
if ( $trigger == $input_trigger ) {
$output[] = $syskey.':'.$input_switch;
continue 3; // continue outer foreach
}
}
}
// if you arrive here, the command was not found in sys
}
$output = implode(',', $output);
echo $output;
PS: The $sys array looks different, but as u say the user sets them up. So there would be no way to check for all cases of "kitchen lights", "kitchen light", and what other stuff the user puts into the array. So they could just fill the array like above, with different triggers for the same thing. I think the ease of use makes up the extra structure of the new $sys. ^^
UPDATE: Updated to allow unordered input. I think the unordered input is kind of hard to deal with, if you can not be sure how many instances of the word "off" or "on" are found in one command. If there are more instances, you won't be able to decide which "on" or "off" is the correct one. There could be a rule.. like "The first instance of "on" or "off" is the one we'll use" or something. The code above will use that rule. So if you input a command like "kitchen off lights on off", it will result in trying to turn OFF the thing that has a trigger "kitchen lights on off". Another possible way is to reject the command if there are more instances of "on"|"off". Or to cut multiple instances of "on"|"off".
Try this:
$values = array();
foreach ($input as $i) {
$parts = explode(' ', $i);
// first word: 'on' || 'off'
$val = array_shift($parts);
// attach the remaining words again to form the key
$key = implode(' ', $parts);
// get the index of the $key value in $sys array
// and concat 0 or 156 depending on $val
$values[] = array_shift(array_keys($sys, $key)).':'.($val == 'on' ? 256: 0);
}
$write = implode(';', $values);
makes use of the second parameter of array_keys to fetch the correct key of the $sys array.
See it in action in this fiddle
edit
For managing different inputs in different formats (without changing the $sys array):
$alts = array(
'kitchen lights' => array(
'kitchen lights', 'kitchen lights', 'lights in kitchen', 'light in kitchen'
),
'living fan' => array(
'living fan', 'living fans', 'fans in living', 'fan in living'
),
);
foreach ($input as $i) {
$i = strtolower($i); // make sure we have all lower caps
// check if on in in the start or beginning of the input
$flag = substr($i, 0, 2) === 'on' || strpos($i, strlen($i)-1, 2) === 'on';
// remove on and off from the string, trim whitespace
$search = trim(str_replace(array('on', 'off'), '', $i));
// search for the resulting string in any of the alt arrays
$foundSysKey = false;
foreach ($alts as $sysKey => $alt) {
if (in_array($search, $alt)) {
$foundSysKey = $sysKey;
break;
}
}
// did not find it? continue to the next one
if ($foundSysKey === false) {
echo 'invalid key: '.$search;
continue;
}
// now you have the info we need and can precede as in the previous example
$values[] = array_shift(array_keys($sys, $foundSysKey)).':'.($flag ? 256: 0);
}
I tried saving an updated fiddle but the site seems to have some problems... it did work though.
Related
This code works great, until something is case sensitive. I've tried using strnatcasecmp instead of the cmp, and it grabs the whole string not the last name. any suggestions on how to write this better?
for example the current order is:
Aaron Carson
Adam Davidson
Jen Hennings
ektor bluemouth
I want ektor bluemouth to be on top.
foreach($connected->posts as $p) {
$lastname = explode(' ', $p->post_title);
if(sizeof($lastname) == 1) {$p->lastname = $lastname[0];}
if(sizeof($lastname) == 2) {$p->lastname = $lastname[1];}
if(sizeof($lastname) == 3) {$p->lastname = $lastname[2];}
}
usort($connected->posts, 'cmp');
I would do something like this
$aPosts = array(
array('post_title' => 'Aaron Carson'),
array('post_title' => 'Adam Davidson'),
array('post_title' => 'Jen Hennings'),
array('post_title' => 'ektor bluemouth'),
);
$aOrderedPosts = array();
foreach($aPosts as $iPos => $p){
$a = explode(' ', $p['post_title']);
// cast the last name to lowercase and assign by reference the post
$aOrderedPosts[strtolower($a[1])] =& $aPosts[$iPos];
}
var_dump($aOrderedPosts);
// perform a key sort
ksort($aOrderedPosts);
var_dump($aOrderedPosts);
It could be adapted to work with your data format
$aOrderedPosts = array();
foreach($connected->posts as $iPos => $p){
$a = explode(' ', $p->post_title);
$aOrderedPosts[strtolower($a[1])] =& $connected->posts[$iPos];
}
ksort($aOrderedPosts);
var_dump($aOrderedPosts);
Best to create $aOrderedPosts when you create/read in the post objects from DB. Additionally I would create it as a property of the connection class.
I need user to enter an array in a textarea and this array might be big array, I tried to serialize it and save it encoded in DB but it failed,
This is my code:
if( strpos( $_POST['textarea_array'], 'array' ) !== FALSE ) {
$temp_serialized = serialize( $_POST['textarea_array'] );
if( ( $temp_unserialized = #unserialize( $temp_serialized ) !== FALSE )
&& is_array( $temp_unserialized ) ) {
/* Condition FAILED */
$temp_json = json_encode( $temp_unserialized );
$final_value = base64_encode( $temp_json );
}
}
Example of what would be entered in the textarea a simple or a complicated array with sub array for each key
array(
'x_sub_array' => array(
'x_1' => 'X 1',
'x_2' => 'X 2',
'x_3' => 'X 3',
);
'x_2' => 'X 2',
'x_3' => 'X 3',
);
First of all, there are easier ways to pass data. But if this is the absolute ONLY way, might as well answer it bluntly. (If your user strictly adheres to the format...)
if(isset($_POST['textarea_array'])){
$raw = $_POST['textarea_array'];
//Parse the array section
$start = strpos($raw, "(");
$end = strpos($raw, ")");
$full = substr($raw,$start+1,$end - ($start+1));
//Remove quotations
$full = str_replace("'","",$full);
//Divide string into array of segments `key=>value` as one segment
$segments = explode(",", $full);
foreach($segments as $segment){
//Divide each segment `key=>value` to a temp array
//Index 0 will hold the key, Index 1 will hold value
$array_part = explode("=>",$segment);
//Remove spaces
$key = trim($array_part[0]);
$value = trim($array_part[1]);
//insert into associative array
$final[$key] = $value;
}
//$final now has your associative array
echo json_encode($final);
}
Again, there are better alternatives.
-How bout having the user pass a json object via AJAX?
-Or how bout the user enter each element and separate with a newline?
But never rely on serialize or eval especially since you dont want users to have access to your php pages.
This may be able to be accomplished with a regular expression but I have no idea. What I am trying to accomplish is being able to parse a string with a given delimiter but when it sees a set of brackets it parses differently. As I am a visual learning let me show you an example of what I am attempting to achieve. (PS this is getting parsed from a url)
Given the string input:
String1,String2(data1,data2,data3),String3,String4
How can I "transform" this string into this array:
{
"String1": "String1",
"String2": [
"data1",
"data2",
"data3"
],
"String3": "String3",
"String4": "String4
}
Formatting doesn't have to be this strict as I'm just attempting to make a simple API for my project.
Obviously things like
array explode ( string $delimiter , string $string [, int $limit = PHP_INT_MAX ] )
Wouldn't work because there are commas inside the brackets as well. I've attempted manual parsing looking at each character at a time but I fear for the performance and it doesn't actually work anyway. I've pasted the gist of my attempt.
https://gist.github.com/Fudge0952/24cb4e6a4ec288a4c492
While you could try to split your initial string on commas and ignore anything in parentheses for the first split, this necessarily makes assumptions about what those string values can actually be (possibly requiring escaping/unescaping values depending on what those strings have to contain).
If you have control over the data format, though, it would be far better to just start with JSON. It's well-defined and well-supported.
You can either build an ad-hoc parser like (mostly untested):
<?php
$p = '!
[^,\(\)]+ # token: String
|, # token: comma
|\( # token: open
|\) # token: close
!x';
$input = 'String1,String2(data1,data2,data3,data4(a,b,c)),String3,String4';
preg_match_all($p, $input, $m);
// using a norewinditerator, so we can use nested foreach-loops on the same iterator
$it = new NoRewindIterator(
new ArrayIterator($m[0])
);
var_export( foo( $it ) );
function foo($tokens, $level=0) {
$result = [];
$current = null;
foreach( $tokens as $t ) {
switch($t) {
case ')':
break; // foreach loop
case '(':
if ( is_null($current) ) {
throw new Exception('moo');
}
$tokens->next();
$result[$current] = foo($tokens, $level+1);
$current = null;
break;
case ',':
if ( !is_null($current) ) {
$result[] = $current;
$current = null;
}
break;
default:
$current = $t;
break;
}
}
if ( !is_null($current) ) {
$result[] = $current;
}
return $result;
}
prints
array (
0 => 'String1',
'String2' =>
array (
0 => 'data1',
1 => 'data2',
2 => 'data3',
'data4' =>
array (
0 => 'a',
1 => 'b',
2 => 'c',
),
),
1 => 'String3',
2 => 'String4',
)
(but will most certainly fail horribly for not-well-formed strings)
or take a look at lexer/parser generator like e.g. PHP_LexerGenerator and PHP_ParserGenerator.
This is a solution with preg_match_all():
$string = 'String1,String2(data1,data2,data3),String3,String4,String5(data4,data5,data6)';
$pattern = '/([^,(]+)(\(([^)]+)\))?/';
preg_match_all( $pattern, $string, $matches );
$result = array();
foreach( $matches[1] as $key => $val )
{
if( $matches[3][$key] )
{ $add = explode( ',', $matches[3][$key] ); }
else
{ $add = $val; }
$result[$val] = $add;
}
$json = json_encode( $result );
3v4l.org demo
Pattern explanation:
([^,(]+) group 1: any chars except ‘,’ and ‘(’
(\(([^)]+)\))? group 2: zero or one occurrence of brackets wrapping:
└──┬──┘
┌──┴──┐
([^)]+) group 3: any chars except ‘,’
I'm working on a search-based website, and am trying to pass parameters using SEO-friendly URLs.
Is it possible to pass the following URL and get the URL in CodeIgniter?
http://www.example.com/search/prize/10/13/14.5/size/xl/2xl/color/black/white-grey/
I am able to create the URL, but I want to get the URL values like $prize = array("10","13","14.5"), $size= array("xl","2xl"), and $color = array("black","white-grey").
I tried to use the uri_to_assoc() function, but it didn't work. I get the following output:
[price] => 10,
[13] => 14.5
...
which is wrong.
Note: I tried to use $this->uri->segment(1), etc., but in this case, the segment position is dynamic.
For example, users may search for only prices of $10, so the URL will get changed to:
http://www.example.com/search/prize/10/size/xl/2xl/color/black/white-grey/
Now the segment position of getting the size must be changed. In this case, I want:
$prize = array("10");
$size = array("xl", "2xl");
$color = array("black", "white-grey");
How can I achieve this?
You are using quite the unconventional "friendly URI" format. Normally when passing parameters there is a single identifier and then the parameter e.g. /name/key/name/key/name/key.
When you use the correct format /name/key/name/key/name/key in conjunction with uri_to_assoc(), you would get:
array(
'name' => 'key',
// etc...
)
but using something like /prize/1/2/3/size/s/m/l/color/black/white/grey would yield:
array(
'prize' => 1,
2 => 3,
'size' => 's',
'm' => 'l',
// etc...
)
Which is useless to you.
You will have to fetch all of the segments individually and build your arrays with a foreach:
$segments = $this->uri->segment_array();
$prize = array();
$size = array();
$color = array();
$curKey = '';
foreach ($segments as $seg) {
if (in_array($seg, array('prize', 'size', 'color'))) {
$curKey = $seg; continue;
}
if (!empty($curKey)) ${$curKey}[] = $seg;
}
// use $prize, $size, and $color as you wish
or alternatively using a multi-dimensional array:
$parameters = array(
'prize' => array(),
'size' => array(),
'color' => array(),
);
$segments = $this->uri->segment_array();
$curKey = '';
foreach ($segments as $seg) {
if (in_array($seg, array('prize', 'size', 'color'))) {
$curKey = $seg; continue;
}
if (!empty($curKey)) $parameters[$curKey][] = $seg;
}
Fetch all the segments
Iterate over them, check each for keywords like size, color, etc. (make a whitelist)
If a keyword is found, assume the segment values until the next keyword segment or the end are the values for that specific keyword (so xl and 2xl will denote the size if preceeded with a keyword size, etc.)
I use foreach to loop the array below and then making the order number to be store in the database,
$items_variable = array(
'page_id',
'page_content_1',
'page_content_2',
'page_content_3',
'page_content_4',
...
);
The code only loop 4 items from the array above (but what if I have these page_content_# increased in the future?)
foreach( $items_variable as $item_variable )
{
if (in_array($item_variable, array(
'page_content_1',
'page_content_2',
'page_content_3',
'page_content_4'
)))
{
if($item_variable == 'page_content_1') $order_in_page = 1;
if($item_variable == 'page_content_2') $order_in_page = 2;
if($item_variable == 'page_content_3') $order_in_page = 3;
if($item_variable == 'page_content_4') $order_in_page = 4;
....
}
}
The current method I have above doesn't look good to me especially when it comes to the line like this,
if($item_variable == 'page_content_1') $order_in_page = 1;
I will add more lines like this when I have page_content_# increased in the future and the script will look pretty ugly I suppose.
What if I have another type of data with order number (for instance - code_1, code_2, etc)? Then I have copy the code above and change the item name each time - this looks pretty gloomy isn't!
How can I make it better and dynamic?
Associative array
You can do this:
$items_definitions = array(
'page_content_1' => 1,
'page_content_2' => 2,
'page_content_3' => 3,
'page_content_4' => 4,
'page_content_5' => 5,
);
foreach( $items_variable as $item_variable ){
if( isset( $items_definitions[ $item_variable])){
$order_in_page = $items_definitions[ $item_variable];
}
...
}
Dynamically extracting last part of string
Or do it completely dynamically assuming that it's always page_content_{$order_in_page}, either with regexp as hackartist suggested or use "oldschool method":
$prefix = 'page_content_';
foreach( $items_variable as $item_variable ){
if( strncmp( $item_variable, $pregix, strlen( $prefix))){
continue; // Assume that you don't want to do anything if it doesn't match
}
$page = intval( substr( $item_variable, strlen( $prefix)));
if( !$page){
continue;
}
$order_in_page = $page;
}
I recommend studying examples from intval() documentation :)
Switch statement
Php provides switch which allows you to handle many different cases with relatively small amount of code.
foreach( $items_variable as $item_variable ){
switch( $item_variable){
case 'page_content_1':
$order_in_page = 1;
break;
case 'page_content_2':
$order_in_page = 2;
break;
case 'page_content_3':
$order_in_page = 3;
break;
...
default:
}
}
I'd however do this only if first two options wouldn't work out for you (eg. you need to call different function for each case).
not sure exactly what you want but try this instead of the if statement:
preg_match('/page_content_([0-9]+)/',$item_variable,$matches);
$order_in_page = $matches[1];
I'm not sure I understand your question, but maybe an associative array would be a solution for you. You can use it to match a string to a value:
$order_in_page = array(
'page_content_1' => 1,
'page_content_2' => 2,
'page_content_3' => 3,
'page_content_4' => 4,
'someotherpage' => 5,
'yet_another_page' => 6
);
$o = $order_in_page[$item_variable];
Stuff on the data structure
http://en.wikipedia.org/wiki/Associative_array
PHP Documentation
http://php.net/manual/de/language.types.array.php
Your current array, if you write it out with the explicit keys, looks like the following:
$items_variable = array(
0 => 'page_id',
1 => 'page_content_1',
2 => 'page_content_2',
3 => 'page_content_3',
4 => 'page_content_4',
...
);
Notice that the key number matches completely with the page content number. Thus, you could change the foreach loop to the following:
foreach( $items_variable as $order_in_page => $item_variable )
Now $order_in_page should be stored with the key number, which in your array correlates directly to the page content number. You may need to cast it into an int, although I am not sure of this fact:
$order_in_page = (int) $order_in_page;
If instead, your array looked like the following (without the 'page_id' element):
$items_variable = array(
0 => 'page_content_1',
1 => 'page_content_2',
2 => 'page_content_3',
3 => 'page_content_4',
...
);
Do the same thing as above, but add one to the result:
++$order_in_page;
If casting is needed, cast before the increment.
foreach($items_variable as $item_variable) {
preg_match('/[0-9]*$/',$item_variable , $suffix);
$suffix = $suffix[0];
if ($suffix !== '') {
# code 1
} else {
# code 2
}
}
I'm not sure I understood your question, but you might want to store your data in a multidimensional array, like:
$items_variable = array(
'page_id',
'content' => array(
//content
)
);
Then you could just iterate through each content-array, such as:
foreach($items_variable['content'] as $content) {
//do stuff with the content
}
No need for regex or other stuff.