Jump to content


Photo

Transform sequential array to associated array with id/name elements


  • Please log in to reply
5 replies to this topic

#1 NotionCommotion

NotionCommotion
  • Members
  • PipPipPip
  • Advanced Member
  • 1,601 posts

Posted 09 May 2018 - 03:22 PM

I need to provide a list of time zones which starts with a given string in id/name pairs, and came up with the following solution.

    public function timezones($param) {
        $timezone_identifiers = \DateTimeZone::listIdentifiers();
        $timezone_identifiers_selected = [];
        array_walk($timezone_identifiers, function($name, $id) use (&$timezone_identifiers_selected, $param) {
            if($this->startsWith($name, $param['term'])) $timezone_identifiers_selected[] = ['id'=>$id, 'name'=>$name];
        });
        return [$timezone_identifiers_selected,200];
    }

    private function startsWith($haystack, $needle) {
        return $needle === "" || stripos($haystack, $needle, -strlen($haystack)) !== false;
    }

Before using array_walk, I used array_filter to come up with the  list, but not transform the sequence array to an associated array with id and name elements.  While I am good with the array_walk solution, I would like to know whether this is possible with any single PHP commands.  Kind of like array_columns but in reverse.  Thanks

‚Äč        $timezone_identifiers_selected = array_filter($timezone_identifiers, function($val) use($param) {
            return $this->startsWith($val, $param['term']);
        });
 

 


NotionCommotion

#2 NotionCommotion

NotionCommotion
  • Members
  • PipPipPip
  • Advanced Member
  • 1,601 posts

Posted 09 May 2018 - 04:06 PM

In hindsight, I won't be using the DateTimeZone::listIdentifiers id so don't need to make the id/value pairs.  Still, however, interested whether it is possible to perform the array transformation.  Thanks


NotionCommotion

#3 requinix

requinix
  • Administrators
  • Impoverished Administrator
  • 9,874 posts
  • LocationWA

Posted 09 May 2018 - 04:31 PM

So, like, you want all timezones that start with "America/" or "Asia/" or something? But why do you want the array key? The array is just a normal indexed array. The key isn't useful.

I don't see a way to get the result with just one or two built-in functions, but you can do it like
public function timezones($param) {
	return [iterator_to_array((function($ids, $term, $termlen) {
		foreach ($ids as $id => $name) {
			if (strncasecmp($name, $term, $termlen) == 0) {
				yield ['id' => $id, 'name' => $name];
			}
		}
	})(\DateTimeZone::listIdentifiers(), $param['term'], strlen($param['term']))), 200];
}
but that's silly. Your solution is fine, although I don't care for startsWith being its own method (plus strncasecmp is better for this than stripos).
"Basically, I think the general rule of thumb is: if someone really wants the blood that's inside of your body, and they're like a vampire, or a dracula, or some sort of man-squito, then that's probably okay. A dracula and a man-squito are made for removing things like blood and swords from inside your body. That's basically fine. If something wants to get at your blood and they're, say, some kind of murdersaurus, or maybe a really big frog, that's where the problems start to arise. A really big frog is not made for removing blood, and your blood knows this, which is why it is so vehement about wanting to stay in your body instead of coming out. Unfortunately this will not deter a really big frog because a really big frog is full of things like prizes, and value, and quite a lot of hatred, and it would really rather like to replace any and all of those things with your blood, and basically by any means possible." --slumbermancer

#4 NotionCommotion

NotionCommotion
  • Members
  • PipPipPip
  • Advanced Member
  • 1,601 posts

Posted 09 May 2018 - 04:53 PM

Thanks requinix,

 

See my previous "In hindsight..." indicating that I don't need the id.  Typically, all my ajax calls use surrogates and originally I wanted to keep consistent, but came to the same conclusion that the key isn't useful.  And I was also on the fence about startsWith being its own method.

 

Why is strncasecmp better for this than stripos?


NotionCommotion

#5 requinix

requinix
  • Administrators
  • Impoverished Administrator
  • 9,874 posts
  • LocationWA

Posted 09 May 2018 - 05:26 PM

Right, saw the reply, promptly forgot about it.

str(n)(case)cmp compares characters in two strings starting at the beginning. If any character doesn't match then the function stops. If it reaches the end of a string, or the length limit for the 'n' version, then the function stops.
str(i)pos searches for whether one string contains another substring. It starts at one point, checks, and if it doesn't match then advances a character and checks again. And repeats until it finds the substring or reaches the end.

In your case you only care if the substring is at the beginning. If it isn't then fail. strncasecmp will do that. stripos will advance to the second character and continue searching, which is wasteful because if it does find the substring you still don't care.
It will also return a false positive because you only care if it returns !==false - it should be ===0 instead.
stripos("America/New_York", "York", -16) === 12

"Basically, I think the general rule of thumb is: if someone really wants the blood that's inside of your body, and they're like a vampire, or a dracula, or some sort of man-squito, then that's probably okay. A dracula and a man-squito are made for removing things like blood and swords from inside your body. That's basically fine. If something wants to get at your blood and they're, say, some kind of murdersaurus, or maybe a really big frog, that's where the problems start to arise. A really big frog is not made for removing blood, and your blood knows this, which is why it is so vehement about wanting to stay in your body instead of coming out. Unfortunately this will not deter a really big frog because a really big frog is full of things like prizes, and value, and quite a lot of hatred, and it would really rather like to replace any and all of those things with your blood, and basically by any means possible." --slumbermancer

#6 Psycho

Psycho
  • Moderators
  • Move along, nothing to see here
  • 11,937 posts
  • LocationCanada

Posted 09 May 2018 - 07:53 PM

While I understand what you originally wanted is no longer needed and you are now just asking for curiosity - I would suggest trying not to "push" a bunch of logic into a single line. Makes it much more difficult to read the code.

 

I would use array_filter() to remove the unwanted entries - then create a new array in the new format you want.


The quality of the responses received is directly proportional to the quality of the question asked.

I do not always test the code I provide, so there may be some syntax errors. In 99% of all cases I found the solution to your problem here: http://www.php.net




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users