Written by Sean Behan on Tue Mar 07th 2017

This is how to reshape an array using Python without Numpy.

This is a fairly simple task but it can be a little confusing to wrap your head around. I ran into this problem while working with Flask, working with array from form fields.

I wanted to use the name attribute as an array In Rails you can use the name[] syntax to create an array forms.

This is useful for dynamically adding form fields to a page.

<input type="text" name="todo[]"/>
<input type="text" name="todo[]"/>
... more ...

On the server we would expect to see todo: ["item A", "item B"] Very convienent.

However, in flask the behavior is a little different. I've seen a few packages that try to solve the problem. But I haven't seen one that compares to Rails. However, the solution is short enough to implement yourself.

Here is the general approach.

headers = ['a', 'b', 'c']

values = [
    ['a0', 'a1'],
    ['b0', 'b1'],
    ['c0', 'c1'],
]

items = [{} for i in range(len(values[0]))]
for x,i in enumerate(values):
    for _x,_i in enumerate(i):
        items[_x][headers[x]] =_i

items == [
    {'a': 'a0', 'b': 'b0', 'c': 'c0'},
    {'a': 'a1', 'b': 'b1', 'c': 'c1'},
]

To use this in Flask we will need to structure our form like this

<form>
    <input name="name[]">
    <input name="price[]">
    <input name="quantity[]">
</form>

I've left out non essential attributes for the example.

And on the server we will use request.form.getlist() to get at the array. Here is where the reshaping code comes into play.

from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/save', methods=["POST"])
def save_path():
    headers = ('name', 'price', 'quantity')
    values = (
        request.form.getlist('name[]'),
        request.form.getlist('price[]'),
        request.form.getlist('quantity[]'),         
    )
    items = [{} for i in range(len(values[0]))]
    for x,i in enumerate(values):
        for _x,_i in enumerate(i):
            items[_x][headers[x]] = _i
    return jsonify(items)

The order of your headers and the order in which you set your values matter because we're using the index of the arrays (with enumerate).

To take this one step further, you could move the logic into a method for reusability and a cleaner API.

def get_list(headers):
    values = [request.form.getlist(h) for h in headers]
    items = [{} for i in range(len(values[0]))]
    for x,i in enumerate(values):
        for _x,_i in enumerate(i):
            items[_x][headers[x]] = _i
    return items

    @app.route("/save", methods=["POST"])
    def save_path():
        return jsonify(get_list(('label', 'price', 'quantity')))
# [
#   {
#     "label": "a", 
#     "price": "a", 
#     "quantity": "a"
#   }, 
#   {
#     "label": "b", 
#     "price": "b", 
#     "quantity": "b"
#   }
# ]

Tagged with..
#Python #Flask #Web Development #Params

Just finishing up brewing up some fresh ground comments...