My code is as follows
foreach($location_total_n_4 as $u=> $v) {
$final_location_total_4 .= "[".$u.",".$v."],";
}
I'm sending these values as JSON.
echo json_encode(array("location"=>"$final_location_total_4" ));
Here's how my response object looks:
{
"location": "[1407110400000,6641],[1407196800000,1566],[1407283200000,3614],"
}
I'm creating graph on success with ajax.so I need it like this,
{
"location": [1407110400000,6641],[1407196800000,1566],[1407283200000,3614],
}
Can anyone help me to solve this?
The problem is that your location value is non-properly serialized value. It's definitely appropriate to fix on the server-side (looks like one's trying to implement their own json_encode and failing), but it's possible to fix on the client-side as well. One possible approach:
var location = JSON.parse('[' + response.location.slice(0,-1) + ']');
Demo. slice(0,-1) removes the trailing comma, then the contents are wrapped into brackets, turning them into a proper JSON (at least for the given dataset).
As for server-side, turned out I was right: this code...
foreach($location_total_n_4 as $u=> $v) {
$final_location_total_4 .= "[".$u.",".$v."],";
}
echo json_encode(array('location' => "$final_location_total_4"));
... is wrong both tactically (always adding a trailing comma) and strategically (one shouldn't solve the task already solved by the language itself). One possible replacement:
$locations = array();
foreach ($location_total_n_4 as $u => $v) {
$locations[] = array($u, $v);
}
echo json_encode(array('location' => $locations));
The bottom line: never attempt to implement your own serialization protocol unless you're really know what're you doing.
There is a trailing comma(,) in the end So json.parse will throw error so we need to remove that.
b = JSON.parse("["+ data[0].substr(0,data[0].length-1) +"]");
Then b becomes
[[1407110400000,6641],[1407196800000,1566],[1407283200000,3614],[1407369600000,3654],[1407456000000,2918],[1407715200000,3900]]
without the trailing comma.
Related
I want to make a personal profile page for my Starcraft 2 Clan with the API in PHP.
The normal stats are working for me.
$json = file_get_contents('http://eu.battle.net/api/sc2/profile/3077083/1/gbot/');
$obj = json_decode($json);
echo $obj->displayName;
However when I'll want to use the ladder stats I can't even display one variable.
$json = file_get_contents('
http://eu.battle.net/api/sc2/profile/3077083/1/gbot/ladders?locale=en_GB');
$lad = json_decode($json);
So how can I display the stats from the child with HOTS_SOLO in it?
This is just basic array access?
$json = file_get_contents('http://eu.battle.net/api/sc2/profile/3077083/1/gbot/ladders?locale=en_GB');
$data= json_decode($json);
$currentSeason = $data->currentSeason;
foreach ($currentSeason as $obj) {
foreach ($obj->ladder as $ladder) {
if ($ladder->matchMakingQueue == 'HOTS_SOLO') {
// this is the ladder we want to display
echo $ladder->ladderName; // Tychus Theta
}
}
}
There appears to be a newline in your URL (it starts on one line, where the whole literal begins on the line before). The file_get_contents() may be failing.
If that's not the problem, then it's something more subtle. Firefox/Chrome don't seem to have a problem with it. If json_decode is choking, it might be a forgiveable syntax issue. Try saving the data locally, and then removing components until it parses, and see if you can then do a string-replace or something to fix it, going forward.
I want to add a query string to a URL, however, the URL format is unpredictable. The URL can be
http://example.com/page/ -> http://example.com/page/?myquery=string
http://example.com/page -> http://example.com/page?myquery=string
http://example.com?p=page -> http://example.com?p=page&myquery=string
These are the URLs I'm thinking of, but it's possible that there are other formats that I'm not aware of.
I'm wondering if there is a standard, library or a common way to do this. I'm using PHP.
Edit: I'm using Cbroe explanation and Passerby code. There is another function by Hamza but I guess it'd be better to use PHP functions and also have cleaner/shorter code.
function addQuery($url,array $query)
{
$cache=parse_url($url,PHP_URL_QUERY);
if(empty($cache)) return $url."?".http_build_query($query);
else return $url."&".http_build_query($query);
}
// test
$test=array("http://example.com/page/","http://example.com/page","http://example.com/?p=page");
print_r(array_map(function($v){
return addQuery($v,array("myquery"=>"string"));
},$test));
Live demo
I'm wondering if there is a standard, library or a common way to do this. I'm using PHP.
Depends on how failsafe – and thereby more complex – you want it to be.
The simplest way would be to look for whether there’s a ? in the URL – if so, append &myquery=string, else append ?myquery=string. This should cover most cases of standards-compliant URLs just fine.
If you want it more complex, you could take the URL apart using parse_url and then parse_str, then add the key myquery with value string to the array the second function returns – and then put it all back together again, using http_build_query for the new query string part.
Some spaghetti Code:
echo addToUrl('http://example.com/page/','myquery', 'string').'<br>';
echo addToUrl('http://example.com/page','myquery', 'string').'<br>';
echo addToUrl('http://example.com/page/wut/?aaa=2','myquery', 'string').'<br>';
echo addToUrl('http://example.com?p=page','myquery', 'string');
function addToUrl($url, $var, $val){
$array = parse_url($url);
if(isset($array['query'])){
parse_str($array['query'], $values);
}
$values[$var] = $val;
unset($array['query']);
$options = '';$c = count($values) - 1;
$i=0;
foreach($values as $k => $v){
if($i == $c){
$options .= $k.'='.$v;
}else{
$options .= $k.'='.$v.'&';
}
$i++;
}
$return = $array['scheme'].'://'.$array['host'].(isset($array['path']) ? $array['path']: '') . '?' . $options;
return $return;
}
Results:
http://example.com/page/?myquery=string
http://example.com/page?myquery=string
http://example.com/page/wut/?aaa=2&myquery=string
http://example.com?p=page&myquery=string
You should try the http_build_query() function, I think that's what you're looking for, and maybe a bit of parse_str(), too.
I have a file which reads as follows
<<row>> 1|test|20110404<</row>>
<<row>> 1|test|20110404<</row>>
<<row>><</row>> indicates start and end of line.I want to read line between this tags and also check whether this tags are present.
The first thing you need to do is locate the position of this "tag". The strpos() function does just that.
$tag_pos=strpos('<> 1|test|20110404<> <> 1|test|20110404<>', '<>');
if ($tag_pos===false) {
//The tag was not found!
} else {
//$tag_pos equals the numeric position of the first character of your tag
}
If these are truly lines, an efficient way to get them all is just to split on <>.
$lines=explode('<>', '<> 1|test|20110404<> <> 1|test|20110404<>');
$lines=array_filter($lines); //Removes blank strings from array
You could improve this by adding a callback function to the array_filter() call that uses trim() to remove any whitespace and then see if it is blank or not.
Edit: Great, I see that your "tags" were missing from your post. Since your start and end tags do not match, the code above will be of little use to you. Let me try again...
function strbetweenstrs($source, $tag1, $tag2, $casesensitive=true) {
$whatsleft=$source;
while ($whatsleft<>'') {
if ($casesensitive) {
$pos1=strpos($whatsleft, $str1);
$pos2=strpos($whatsleft, $str2, $pos1+strlen($str1));
} else {
$pos1=strpos(strtoupper($whatsleft), strtoupper($str1));
$pos2=strpos(strtoupper($whatsleft), strtoupper($str2), $pos1+strlen($str1));
}
if (($pos1===false) || ($pos2===false)) {
break;
}
array_push($results, substr($whatsleft, $pos1+strlen($str1), $pos2-($pos1_strlen($str1))));
$whatsleft=substr($whatsleft, $pos2+strlen($str2));
}
}
Note that I haven't tested this... but you get the generally idea. There is probably a much more efficient way to go about doing it.
Creating your own format is not so hard, but creating a script to read it can be difficult.
The advantage of using standardized formats is that most programming languages has support for them already. For example:
XML: You can use the simplexml_load_string() function and it can make you navigate easily through your content.
$str = "<?xml version="1.0" encoding="utf-8"?>
<data>
<row>1|test|20110404</row>
<row>1|test|20110404</row>
</data>";
$xml = simplexml_load_string($str);
Now you can access your data
echo $xml->row[0];
echo $xml->row[1];
i'm sure you get the idea,
there is also a very good support for JSON (Javascript Object Notation) using the jsondecode() function;
Check it on php.net for more details
i would suggest to use preg_match :-
preg_match( '#<< row>>(.*)<< /row>>#', $line, $matches);
if( ! empty($matches))
{
// line was found
print_r( $matches[1] ); // will contain the content between the start and end row tags
}
I have a text ($text) and an array of words ($tags). These words in the text should be replaced with links to other pages so they don't break the existing links in the text. In CakePHP there is a method in TextHelper for doing this but it is corrupted and it breaks the existing HTML links in the text. The method suppose to work like this:
$text=Text->highlight($text,$tags,'\1',1);
Below there is existing code in CakePHP TextHelper:
function highlight($text, $phrase, $highlighter = '<span class="highlight">\1</span>', $considerHtml = false) {
if (empty($phrase)) {
return $text;
}
if (is_array($phrase)) {
$replace = array();
$with = array();
foreach ($phrase as $key => $value) {
$key = $value;
$value = $highlighter;
$key = '(' . $key . ')';
if ($considerHtml) {
$key = '(?![^<]+>)' . $key . '(?![^<]+>)';
}
$replace[] = '|' . $key . '|ix';
$with[] = empty($value) ? $highlighter : $value;
}
return preg_replace($replace, $with, $text);
} else {
$phrase = '(' . $phrase . ')';
if ($considerHtml) {
$phrase = '(?![^<]+>)' . $phrase . '(?![^<]+>)';
}
return preg_replace('|'.$phrase.'|i', $highlighter, $text);
}
}
You can see (and run) this algorithm here:
http://www.exorithm.com/algorithm/view/highlight
It can be made a little better and simpler with a few changes, but it still isn't perfect. Though less efficient, I'd recommend one of Ben Doom's solutions.
Replacing text in HTML is fundamentally different than replacing plain text. To determine whether text is part of an HTML tag requires you to find all the tags in order not to consider them. Regex is not really the tool for this.
I would attempt one of the following solutions:
Find the positions of all the words. Working from last to first, determine if each is part of a tag. If not, add the anchor.
Split the string into blocks. Each block is either a tag or plain text. Run your replacement(s) on the plain text blocks, and re-assemble.
I think the first one is probably a bit more efficient, but more prone to programmer error, so I'll leave it up to you.
If you want to know why I'm not approaching this problem directly, look at all the questions on the site about regex and HTML, and how regex is not a parser.
This code works just fine. What you may need to do is check the CSS for the <span class="highlight"> and make sure it is set to some color that will allow you to distinguish that it is high lighted.
.highlight { background-color: #FFE900; }
Amorphous - I noticed Gert edited your post. Are the two code fragments exactly as you posted them?
So even though the original code was designed for highlighting, I understand you're trying to repurpose it for generating links - it should, and does work fine for that (tested as posted).
HOWEVER escaping in the first code fragment could be an issue.
$text=Text->highlight($text,$tags,'\1',1);
Works fine... but if you use speach marks rather than quote marks the backslashes disappear as escape marks - you need to escape them. If you don't you get %01 links.
The correct way with speach marks is:
$text=Text->highlight($text,$tags,"\\1",1);
(Notice the use of \1 instead of \1)
UPDATE:
Thank you all for your input. Some additional information.
It's really just a small chunk of markup (20 lines) I'm working with and had aimed to to leverage a regex to do the work.
I also do have the ability to hack up the script (an ecommerce one) to insert the classes as the navigation is built. I wanted to limit the number of hacks I have in place to keep things easier on myself when I go to update to the latest version of the software.
With that said, I'm pretty aware of my situation and the various options available to me. The first part of my regex works as expected. I posted really more or less to see if someone would say, "hey dummy, this is easy just change this....."
After coming close with a few of my efforts, it's more of the principle at this point. To just know (and learn) a solution exists for this problem. I also hate being beaten by a piece of code.
ORIGINAL:
I'm trying to leverage regular expressions to add a CSS a class to the first and last list items within an ordered list. I've tried a bunch of different ways but can't produce the results I'm looking for.
I've got a regular expression for the first list item but can't seem to figure a correct one out for the last. Here is what I'm working with:
$patterns = array('/<ul+([^<]*)<li/m', '/<([^<]*)(?<=<li)(.*)<\/ul>/s');
$replace = array('<ul$1<li class="first"','<li class="last"$2$3</ul>');
$navigation = preg_replace($patterns, $replace, $navigation);
Any help would be greatly appreciated.
Jamie Zawinski would have something to say about this...
Do you have a proper HTML parser? I don't know if there's anything like hpricot available for PHP, but that's the right way to deal with it. You could at least employ hpricot to do the first cleanup for you.
If you're actually generating the HTML -- do it there. It looks like you want to generate some navigation and have a .first and .last kind of thing on it. Take a step back and try that.
+1 to generating the right html as the best option.
But a completely different approach, which may or may not be acceptable to you: you could use javascript.
This uses jquery to make it easy ...
$(document).ready(
function() {
$('#id-of-ul:firstChild').addClass('first');
$('#id-of-ul:lastChild').addClass('last');
}
);
As I say, may or may not be any use in this case, but I think its a valid solution to the problem in some cases.
PS: You say ordered list, then give ul in your example. ol = ordered list, ul = unordered list
You wrote:
$patterns = array('/<ul+([^<]*)<li/m','/<([^<]*)(?<=<li)(.*)<\/ul>/s');
First pattern:
ul+ => you search something like ullll...
The m modifier is useless here, since you don't use ^ nor $.
Second pattern:
Using .* along with s is "dangerous", because you might select the whole document up to the last /ul of the page...
And well, I would just drop s modifier and use: (<li\s)(.*?</li>\s*</ul>) with replace: '$1class="last" $2'
In view of above remarks, I would write the first expression: <ul.*?>\s*<li
Although I am tired of seeing the Jamie Zawinski quote each time there is a regex question, Dustin is right in pointing you to a HTML parser (or just generating the right HTML from the start!): regexes and HTML doesn't mix well, because HTML syntax is complex, and unless you act on a well known machine generated output with very predictable result, you are prone to get something breaking in some cases.
I don't know if anyone cares any longer, but I have a solution that works in my simple test case (and I believe it should work in the general case).
First, let me point out two things: While PhiLho is right in that the s is "dangerous", since dots may match everything up to the final of the document, this may very well be what you want. It only becomes a problem with not well formed pages. Be careful with any such regex on large, manually written pages.
Second, php has a special meaning of backslashes, even in single quotes. Most regexen will perform well either way, but you should always double-escape them, just in case.
Now, here's my code:
<?php
$navigation='<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
<li>Beer</li>
<li>Water</li>
</ul>';
$patterns = array('/<ul.*?>\\s*<li/',
'/<li((.(?<!<li))*?<\\/ul>)/s');
$replace = array('$0 class="first"',
'<li class="last"$1');
$navigation = preg_replace($patterns, $replace, $navigation);
echo $navigation;
?>
This will output
<ul>
<li class="first">Coffee</li>
<li>Tea</li>
<li>Milk</li>
<li>Beer</li>
<li class="last">Water</li>
</ul>
This assumes no line feeds inside the opening <ul...> tag. If there are any, use the s modifier on the first expression too.
The magic happens in (.(?<!<li))*?. This will match any character (the dot) that is not the beginning of the string <li, repeated any amount of times (the *) in a non-greedy fashion (the ?).
Of course, the whole thing would have to be expanded if there is a chance the list items already have the class attribute set. Also, if there is only one list item, it will match twice, giving it two such attributes. At least for xhtml, this would break validation.
You could load the navigation in a SimpleXML object and work with that. This prevents you from breaking your markup with some crazy regex :)
As a preface .. this is waaay over-complicating things in most use-cases. Please see other answers for more sanity :)
Here is a little PHP class I wrote to solve a similar problem. It adds 'first', 'last' and any other classes you want. It will handle li's with no "class" attribute as well as those that already have some class(es).
<?php
/**
* Modify list items in pre-rendered html.
*
* Usage Example:
* $replaced_text = ListAlter::addClasses($original_html, array('cool', 'awsome'));
*/
class ListAlter {
private $classes = array();
private $classes_found = FALSE;
private $count = 0;
private $total = 0;
// No public instances.
private function __construct() {}
/**
* Adds 'first', 'last', and any extra classes you want.
*/
static function addClasses($html, $extra_classes = array()) {
$instance = new self();
$instance->classes = $extra_classes;
$total = preg_match_all('~<li([^>]*?)>~', $html, $matches);
$instance->total = $total ? $total : 0;
return preg_replace_callback('~<li([^>]*?)>~', array($instance, 'processListItem'), $html);
}
private function processListItem($matches) {
$this->count++;
$this->classes_found = FALSE;
$processed = preg_replace_callback('~(\w+)="(.*?)"~', array($this, 'appendClasses'), $matches[0]);
if (!$this->classes_found) {
$classes = $this->classes;
if ($this->count == 1) {
$classes[] = 'first';
}
if ($this->count == $this->total) {
$classes[] = 'last';
}
if (!empty($classes)) {
$processed = rtrim($matches[0], '>') . ' class="' . implode(' ', $classes) . '">';
}
}
return $processed;
}
private function appendClasses($matches) {
array_shift($matches);
list($name, $value) = $matches;
if ($name == 'class') {
$value = array_filter(explode(' ', $value));
$value = array_merge($value, $this->classes);
if ($this->count == 1) {
$value[] = 'first';
}
if ($this->count == $this->total) {
$value[] = 'last';
}
$value = implode(' ', $value);
$this->classes_found = TRUE;
}
return sprintf('%s="%s"', $name, $value);
}
}