Commits

Christian Wyglendowski committed 4cd9572

Added the .map traversal method, README examples and a unit test case.

  • Participants
  • Parent commits bdfa559

Comments (0)

Files changed (3)

pyquery/README.txt

     >>> d('p').not_('.hello')
     [<p#test>]
 
+You can map a callable onto a PyQuery and get a mutated result. The result can
+contain any items, not just elements::
+
+    >>> d('p').map(lambda i, e: PyQuery(e).text())
+    ['you know Python rocks', 'hello python !']
+
+Like the filter method, map callbacks can reference the current item as this::
+
+    >>> d('p').map(lambda i, e: len(PyQuery(this).text()))
+    [21, 14]
+
+The map callback can also return a list, which will extend the resulting
+PyQuery::
+
+    >>> d('p').map(lambda i, e: PyQuery(this).text().split())
+    ['you', 'know', 'Python', 'rocks', 'hello', 'python', '!']
+
 It is possible to select a single element with eq::
 
     >>> d('p').eq(0)

pyquery/pyquery.py

 
     def __repr__(self):
         r = []
-        for el in self:
-            c = el.get('class')
-            c = c and '.' + '.'.join(c.split(' ')) or ''
-            id = el.get('id')
-            id = id and '#' + id or ''
-            r.append('<%s%s%s>' % (el.tag, id, c))
-        return '[' + (', '.join(r)) + ']'
+        try:
+            for el in self:
+                c = el.get('class')
+                c = c and '.' + '.'.join(c.split(' ')) or ''
+                id = el.get('id')
+                id = id and '#' + id or ''
+                r.append('<%s%s%s>' % (el.tag, id, c))
+            return '[' + (', '.join(r)) + ']'
+        except AttributeError:
+            return list.__repr__(self)
+
 
     ##############
     # Traversing #
             func(self.__class__([e]))
         return self
 
+    def map(self, func):
+        """Returns a new PyQuery after transforming current items with func.
+
+        func should take two arguments - 'index' and 'element'.  Elements can 
+        also be referred to as 'this' inside of func.
+        """
+        items = []
+        try:
+            for i, element in enumerate(self):
+                func.func_globals['this'] = element
+                result = func(i, element)
+                if result is not None:
+                    if not isinstance(result, list):
+                        items.append(result)
+                    else:
+                        items.extend(result)
+        finally:
+            del func.func_globals['this']
+        return self.__class__(items, **dict(parent=self))
+
     @property
     def length(self):
         return len(self)
         assert len(self.klass('#node2', self.html).find('span')) == 2
         assert len(self.klass('div', self.html).find('span')) == 3
 
+    def test_map(self):
+        def ids_minus_one(i, elem):
+            return int(self.klass(elem).attr('id')[-1]) - 1
+        assert self.klass('div', self.html).map(ids_minus_one) == [0, 1]
+
     def test_end(self):
         assert len(self.klass('div', self.html).find('span').end()) == 2
         assert len(self.klass('#node2', self.html).find('span').end()) == 1