Skip to content

Fix headers for list of dicts #247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,35 @@ Alice 24
Bob 19
```

When the data is a list of dict, `headers="firstrow"` can also be
used to assign different column names to each corresponding dict key:

```pycon
>>> print(tabulate([{"name": "Name", "age": "Age"},
{"name": "Alice", "age": 24},
{"name": "Bob", "age": 19}],
headers="firstrow"))
Name Age
------ -----
Alice 24
Bob 19
```

Furthermore with a list of dict, you can also specify `headers` as
a dict (similar to the example above), or as a list of keys. Either way,
the specified keys can be a subset of all the keys present across the
dataset:

```pycon
>>> print(tabulate([{"foo": 1, "bar": 2},
{"foo": 3, "bar": 4, "baz": 5}],
headers=["bar","foo"]))
bar foo
----- -----
2 1
4 3
```

If `headers="keys"`, then the keys of a dictionary/dataframe, or column
indices are used. It also works for NumPy record arrays and lists of
dictionaries or named tuples:
Expand Down
58 changes: 27 additions & 31 deletions tabulate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
except ImportError:
wcwidth = None

try:
_string_type = basestring
except:
_string_type = str

def _is_file(f):
return isinstance(f, io.IOBase)
Expand Down Expand Up @@ -1410,37 +1414,29 @@ def _normalize_tabular_data(tabular_data, headers, showindex="default"):
headers = list(map(str, rows[0]._fields))
elif len(rows) > 0 and hasattr(rows[0], "keys") and hasattr(rows[0], "values"):
# dict-like object
uniq_keys = set() # implements hashed lookup
keys = [] # storage for set

if headers == "firstrow":
firstdict = rows[0] if len(rows) > 0 else {}
keys.extend(firstdict.keys())
uniq_keys.update(keys)
headers = rows[0] if len(rows) > 0 else {}
rows = rows[1:]
for row in rows:
for k in row.keys():
# Save unique items in input order
if k not in uniq_keys:
keys.append(k)
uniq_keys.add(k)
if headers == "keys":
headers = keys
elif isinstance(headers, dict):
# a dict of headers for a list of dicts
headers = [headers.get(k, k) for k in keys]
headers = list(map(str, headers))
elif headers == "firstrow":
if len(rows) > 0:
headers = [firstdict.get(k, k) for k in keys]
headers = list(map(str, headers))
else:
headers = []
elif headers:
raise ValueError(
"headers for a list of dicts is not a dict or a keyword"
)
rows = [[row.get(k) for k in keys] for row in rows]

if isinstance(headers,_string_type):
# list unique keys in input order
uniq_keys = set() # implements hashed lookup
keys = [] # storage for set
for row in rows:
for k in row.keys():
if k not in uniq_keys:
keys.append(k)
uniq_keys.add(k)
elif hasattr(headers,'keys') and hasattr(headers,'values'):
# dict-like { key => header name }
keys = list(headers.keys())
headers = list(headers.values())
else:
# list-like [key1, key2, ...]
keys = list(headers)

rows = [[row.get(k) for k in keys] for row in rows]
elif (
headers == "keys"
and hasattr(tabular_data, "description")
Expand Down Expand Up @@ -1499,10 +1495,10 @@ def _normalize_tabular_data(tabular_data, headers, showindex="default"):

# pad with empty headers for initial columns if necessary
if headers and len(rows) > 0:
nhs = len(headers)
nhead = len(headers)
ncols = len(rows[0])
if nhs < ncols:
headers = [""] * (ncols - nhs) + headers
if nhead != ncols:
raise RuntimeError('Number of headers and columns do not match.')

return rows, headers

Expand Down