Skip to content
This repository
Browse code

Basic path selectors implemented for extract().

  • Loading branch information...
commit 37181e99a80b897dd11ed246b500dedde8f3d58b 1 parent b3861c0
Mark Story authored January 22, 2012
50  lib/Cake/Test/Case/Utility/Set2Test.php
@@ -130,6 +130,9 @@ public function testGet() {
130 130
 		$result = Set2::get($data, '');
131 131
 		$this->assertNull($result);
132 132
 
  133
+		$result = Set2::get($data, '0.Article.title');
  134
+		$this->assertEquals('First Article', $result);
  135
+
133 136
 		$result = Set2::get($data, '1.Article.title');
134 137
 		$this->assertEquals('Second Article', $result);
135 138
 
@@ -577,4 +580,51 @@ public function testNumeric() {
577 580
 		$data = array('one', 2 => 'two', 3 => 'three', 4 => 'four', 'a' => 'five');
578 581
 		$this->assertFalse(Set::numeric(array_keys($data)));
579 582
 	}
  583
+
  584
+/**
  585
+ * Test simple paths.
  586
+ *
  587
+ * @return void
  588
+ */
  589
+	public function testExtractBasic() {
  590
+		$data = self::articleData();
  591
+
  592
+		$result = Set2::extract($data, '');
  593
+		$this->assertEquals($data, $result);
  594
+
  595
+		$result = Set2::extract($data, '0.Article.title');
  596
+		$this->assertEquals(array('First Article'), $result);
  597
+
  598
+		$result = Set2::extract($data, '1.Article.title');
  599
+		$this->assertEquals(array('Second Article'), $result);
  600
+	}
  601
+
  602
+	public function testExtractNumericKey() {
  603
+		$data = self::articleData();
  604
+		$result = Set2::extract($data, '{n}.Article.title');
  605
+		$expected = array(
  606
+			'First Article', 'Second Article', 
  607
+			'Third Article', 'Fourth Article',
  608
+			'Fifth Article'
  609
+		);
  610
+		$this->assertEquals($expected, $result);
  611
+
  612
+		$result = Set2::extract($data, '0.Comment.{n}.user_id');
  613
+		$expected = array(
  614
+			'2', '4'
  615
+		);
  616
+		$this->assertEquals($expected, $result);
  617
+	}
  618
+	
  619
+	public function testExtractStringKey() {
  620
+		$data = self::articleData();
  621
+		$result = Set2::extract($data, '{n}.{s}.user');
  622
+		$expected = array(
  623
+			'mariano', 'mariano', 
  624
+			'mariano', 'mariano',
  625
+			'mariano'
  626
+		);
  627
+		$this->assertEquals($expected, $result);
  628
+	}
  629
+
580 630
 }
95  lib/Cake/Utility/Set2.php
@@ -44,18 +44,109 @@ public static function get(array $data, $path) {
44 44
 			return null;
45 45
 		}
46 46
 		$parts = explode('.', $path);
47  
-		while ($key = array_shift($parts)) {
48  
-			if (isset($data[$key])) {
  47
+		while (($key = array_shift($parts)) !== null) {
  48
+			if (is_array($data) && isset($data[$key])) {
49 49
 				$data =& $data[$key];
50 50
 			} else {
51 51
 				return null;
52 52
 			}
  53
+			
53 54
 		}
54 55
 		return $data;
55 56
 	}
56 57
 
  58
+/**
  59
+ * Gets the values from an array matching the $path expression.
  60
+ * The path expression is a dot separated expression, that can contain a set
  61
+ * of patterns and expressions:
  62
+ *
  63
+ * - `{n}` Matches any numeric key.
  64
+ * - `{s}` Matches any string key.
  65
+ * - `[id]` Matches elements with an `id` index.
  66
+ * - `[id>2]` Matches elements that have an `id` index greater than 2.  Other operators 
  67
+ *   are `>`, `<`, `<=`, `>=`, `==`, and `=//` which allows you to use regular expression matching.
  68
+ *
  69
+ * Given a set of User array data, from a `$User->find('all')` call:
  70
+ *
  71
+ * - `1.User.name` Get the name of the user at index 1.
  72
+ * - `{n}.User.name` Get the name of every user in the set of users.
  73
+ * - `{n}.User[id]` Get the name of every user with an id key.
  74
+ * - `{n}.User[id>=2]` Get the name of every user with an id key greater than or equal to 2.
  75
+ * - `{n}.User[username=/^paul/]` Get User elements with username matching `^paul`.
  76
+ *
  77
+ * @param array $data The data to extract from.
  78
+ * @param string $path The path to extract.
  79
+ * @return array An array of the extracted values.  Returns an empty array 
  80
+ *   if there are no matches.
  81
+ */
57 82
 	public static function extract(array $data, $path) {
  83
+		if (empty($path)) {
  84
+			return $data;
  85
+		}
  86
+
  87
+		// Simple paths.
  88
+		if (!preg_match('/[{\[]/', $path)) {
  89
+			return (array) self::get($data, $path);
  90
+		}
  91
+
  92
+		return self::_traverse($data, $path, function ($value) {
  93
+			return $value;
  94
+		});
  95
+	}
  96
+
  97
+/**
  98
+ * Traverses $data for $path.  $callback is called for each terminal element.
  99
+ * The results of all the callbacks are returned.
  100
+ */
  101
+	protected static function _traverse(array &$data, $path, $callback) {
  102
+		$result = array();
  103
+		$tokens = String::tokenize($path, '.', '{', '}');
  104
+
  105
+		$_key = '__set_item__';
  106
+
  107
+		$context = array($_key => array($data));
  108
+
  109
+		do  {
  110
+			$token = array_shift($tokens);
  111
+			$next = array();
  112
+
  113
+			foreach ($context[$_key] as $item) {
  114
+				if ($token === '{n}') {
  115
+					// any numeric key
  116
+					foreach ($item as $k => $v) {
  117
+						if (is_numeric($k)) {
  118
+							$next[] = $v;
  119
+						}
  120
+					}
  121
+				} elseif ($token === '{s}') {
  122
+					// any string key
  123
+					foreach ($item as $k => $v) {
  124
+						if (is_string($k)) {
  125
+							$next[] = $v;
  126
+						}
  127
+					}
  128
+				} elseif (is_numeric($token)) {
  129
+					// numeric keys like 0, 1, 2
  130
+					foreach ($item as $k => $v) {
  131
+						if ($k == $token) {
  132
+							$next[] = $v;
  133
+						}
  134
+					}
  135
+				} else {
  136
+					// bare string key
  137
+					foreach ($item as $k => $v) {
  138
+						// index or key match.
  139
+						if ($k === $token) {
  140
+							$next[] = $v;
  141
+						}
  142
+					}
  143
+				}
  144
+			}
  145
+			$context = array($_key => $next);
  146
+
  147
+		} while (!empty($tokens));
58 148
 
  149
+		return array_map($callback, $context[$_key]);
59 150
 	}
60 151
 
61 152
 	public static function insert(array $data, $path, $values = null) {

0 notes on commit 37181e9

Please sign in to comment.
Something went wrong with that request. Please try again.