0

In the code below, I am creating a "fake" dataframe from JSON response, for the purpose of Python-like data manipulation.

Is there a less verbose way to do it than what is posted below?

Basically, I need to put an array of objects into single object. All objects are of the same structure.

<script>
    //~ request API for data
    function get_data(callback){
        $.ajax({type:"GET", 
                url: "{% url 'explorer_api:list_activities' %}",
                //~ success: function(data){
                    //~ console.log(data[1])},
                success: callback,
                headers: {'X-CSRFToken': '{{ csrf_token }}'},

        });
    }

    //~ get data and process it
    get_data(function(data) {

        //create dataframe
        function create_dataframe() {
            var activities = data;                  
            var df = {
                number: [],
                athlete: [],
                start_date: [],
                name: [],   
                country: [],
                link: [],
                segment_efforts:[],
                distance: [],
                average_speed: [],
            };

            for (i in activities) {
                df.number.push(activities[i].number);
                df.athlete.push(activities[i].athlete);
                df.start_date.push(activities[i].start_date);
                df.name.push(activities[i].name);
                df.country.push(activities[i].country);
                df.link.push(activities[i].link);
                df.segment_efforts.push(activities[i].segment_efforts);
                df.distance.push(activities[i].distance);
                df.average_speed.push(activities[i].average_speed);
            };
            return df;
        };
        df = create_dataframe()
        console.log(df);
    }); 
</script>
barciewicz
  • 3,511
  • 6
  • 32
  • 72
  • Looks like a cumbersome way to manage data in javascript. What is your "manipulation" use case? – charlietfl Dec 15 '18 at 20:31
  • @charlietfl Agree. The data is first received by above AJAX request, visualized, and then filtered when user interacts with dropdowns, sliders etc. I would love to do the filtering logic in Python, which I am using for backend, but don't know how to do this other than sending the received data from the front to backend, filter it, and send to front again - that is a lot of data flying around, so the visualization might be unresponsive. – barciewicz Dec 15 '18 at 20:36
  • OK but is easy to filter or sort arrays of objects and keep all those properties within each object....in other words manipulate the original data. Might boil down to what you mean by "visualize" – charlietfl Dec 15 '18 at 20:37

4 Answers4

0

You can use another for-in loop to loop through your object's properties instead of writing out each line.

for(i in activities) {
    for(var prop in df) {
        df[prop].push(activities[i][prop]);
    }
}

I would also recommend using a traditional for loop or another method like .forEach() to loop through activities, since for-in is not recommended for arrays.

Hydrothermal
  • 4,851
  • 7
  • 26
  • 45
0

You can use Object.entries to iterate over the keys in the object. Example:

var activities = [{ number: 1, test: 2 }, { number: 2, test: 3 }]

var df = {}

for (var activity of activities) {
  for (var [key, value] of Object.entries(activity)) {
    if (!df[key]) df[key] = []
    df[key].push(value)
  }
}
df // { number: [1,2], test: [2,3] }
Sam Denty
  • 3,693
  • 3
  • 30
  • 43
0

Kind of expanding on Hydrothermal's answer here. Instead of hard coding the values you could make it more dynamic by iterating through the keys in the object and setting the values as such with a simple reduce and a forEach inside of that. Here is the code for that:

const arr = [{id: 0, value: 'foo'},{id: 1, value: 'bar'},{id: 2, value: 'baz'}];

const obj = arr.reduce((initial, value, index) => {
  const keys = Object.keys(value);
  keys.forEach(key => {
    if(!initial[key]) initial[key] = [value[key]];
    else initial[key].push(value[key]);
  });
  return initial;
}, {});

console.log(obj)

As far as verboseness, this doesn't change much but I think this is a better and more dynamic implementation than the one in your question.

Cory Kleiser
  • 1,969
  • 2
  • 13
  • 26
0

This is a functional (ES6) approach:

data.reduce((a,c) => { Object.keys(c).map(k => a[k] = a[k] ? [...a[k],c[k]] : [c[k]]); return a; }, {})

Although I would not bet on any performance gain compared to the imperative approach in case of JavaScript.

[Update]

Here is an imperative one which is the "closest" to the functional above:

result = { }
for(var e of DATA){
   for(var p in e){
      if(result[p]) { result[p].push(e[p]) } else { result[p] = [e[p]] }
   }
}

As you might have noticed, my proposed solutions do not have any preconceptions about the objects in the array - like having the same structure. This presents some overhead in checking for the property in the result.

But, as I suspected and as I already noticed in some scenarios, the functional side of JavaScript is not really elaborated from the performance point of view.

But I wanted to see myself, so I have put the above two snippets at test here: https://jsperf.com/ao-to-oa-imp-func/1

And the results are more than convincing for me: the functional code is 93% slower than the imperative one in my Chrome, and 99% slower in Firefox.

ZorgoZ
  • 2,974
  • 1
  • 12
  • 34