4

I am witnessing some magic over here.

First of all, this is a pretty common question and believe me I've done my research, so please before you mark this as duplicate, first hear me out.

The difference in performance between arrays and objects has been brilliantly explained in this SO answer and I've applied it to my React Native application with the hopes that it will improve my performance by about 300%. But the results are a huge, disappointing surprise.

I changed my data structure from an array of objects...

let poems = [ obj1, obj2, ..., obj64]

to an object with keys as object ids and values as the whole objects themselves

let poems = { "obj1_id": obj1, "obj2_id": obj2, ...}

Now if I want to access an object, instead of looping through the array and testing for quality of say id attribute, I can instead just do this: poems[obj1_id] and this will be far more efficient (speed wise).

No matter how I think about it, I don't see any reason why this should not be the case. So now I want to share my source code (React Native) because I suspect that the problem might be with the way I'm implementing.

Here is my data

poems.json:

{
"Family": {
  "1":  {
    "id": "1",
    "category": "Family",
    "favorite": false,
    "body": "Poem 1"
  },
  .
  .
  .
  "64": {
    "id": "64",
    "category": "Family",
    "favorite": false,
    "body": "Poem 64"
  }
}
}

This is my reducer: poemReducer.js

  import all_poems from '../data/poems_obj.json';

  const initialPoems = {
      allPoems: all_poems,
      currentPoems: {},
      favoritePoems: {}
  };

  export default poems = (state = initialPoems, action = {}) => {
    switch (action.type) {
      case SET_POEMS:
        return state;
      case SET_CURRENT_POEMS:
        return {
          ...state,
          currentPoems: state.allPoems[action.name]
        };
      case TOGGLE_FAVORITE:
        let currentPoems = state.currentPoems;
        let favoritePoems = state.favoritePoems;

        let poem = currentPoems[action.id];
        poem.favorite = !poem.favorite;

        if (poem.favorite) {
          favoritePoems[poem.id] = poem;
        } else {
          delete favoritePoems[poem.id]
        }
        return { ...state, currentPoems, favoritePoems };
        default: return state;
    }
  }

Believe it or not, when I click on the favorite button to toggle a poem's favorite boolean value, it takes approximately 9s. Not 9ms, 9 frigging SECONDS in an object with a size of only 64 objects.

Here is how I dispatch the actions:

  export const toggleFavorite = (id) => {
return {
  type: TOGGLE_FAVORITE,
  id
};
}

Am I doing something wrong? Is there something I don't get about the overall idea.

Any insights will be very much appreciated.

EDIT

Sorry if this wasted anybody's time. I realize I was making a mistake in an entirely different part of my application. In the React Native componenent of my application responsible for rendering the poems, I was doing something like this:

First I got the poems from redux:

 mapStateToProps = (state) => { poems: state.poems.currentPoems }

Since the poems exist in redux as objects, these poems come in the is format:

 {
  "1":  {
    "id": "1",
    "category": "Family",
    "favorite": false,
    "body": "Poem 1"
  },
  .
  .
  .
  "64": {
    "id": "64",
    "category": "Family",
    "favorite": false,
    "body": "Poem 64"
  }
}

I am rendering this in a FlatList component in React Native so I need this data to be an array of objects. So to convert the object into an array of objects, I do this just before the return statement in my render() method:

const { poems } = this.props; // grab from redux, via mapStateToProps function
const poemsArr = Object.keys(poems).map(key => peoms[key]); // change into array of objects

then I proceed to pass this poemsArr as value to the data attribute of my Flatlist component.

  <FlatList
    data={poemsArr}
    renderItem={this.renderItem}
  />

Somehow this slowed down my application big time.

Now the change I made that got my code to be working properly was that instead of converting my object to array of objects inside the render() method, I did it directly in the mapStateToProps() method:

mapStateToProps = (state) => { poems: Object.keys(currentPoems).map(key => currentPoems[key]) }

That solved my problem. I don't have a good explanation for why it works yet. That's my assignment for now.

I'm sorry if this wasted anybody's time. Thanks for your contributions. They really helped me.

P.S.: I did a lot of modifications on my original source code before posting it here to keep it general and simple. There might be some syntax errors as I am only a beginner JavaScript developer. Thanks

Awa Melvine
  • 3,797
  • 8
  • 34
  • 47
  • 1
    Please update your question with a [mcve] demonstrating the problem, ideally a **runnable** one using Stack Snippets (the `[<>]` toolbar button). Stack Snippets support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). – T.J. Crowder Aug 06 '18 at 10:41
  • 1
    What has your debugger and/or profiler told you? Which part is responsible for that 9s? – Andreas Aug 06 '18 at 10:44
  • I am using my phone to test the application. My phone: Samsung Galaxy S5, 1GB RAM. I'm not sure exactly what you mean by debugger/profiler but I am using chrome to debug remotely and only test functionality not performance. Everything works as expected, exept the performance. – Awa Melvine Aug 06 '18 at 10:48
  • 1
    you are mutating the state in your TOGGLE_FAVORITE case – webmaster Aug 06 '18 at 11:06
  • @webmaster thanks for that observation. Now that you mention it I realize my mistake. I mistook assigning with = for actually producing a copy. Is this what I'm supposed to do instead: `let currentPoems = Object.assign({}, state.currentPoems);`? – Awa Melvine Aug 06 '18 at 12:06
  • 1
    you shouldn't directly assign values to the found object, it will mutate the state, 'poem.favorite = !poem.favorite;' or favoritePoems[poem.id] = poem; is wrong.. – webmaster Aug 07 '18 at 11:51
  • 1
    Instead do the following; const poem = { ...currentPoems[action.id]; favorite: !currentPoems[action.id].favorite } – webmaster Aug 07 '18 at 11:53
  • Okay. Thank you! – Awa Melvine Aug 07 '18 at 15:04
  • @AwaMelvine I added an answer. I hope it helps as per the problem statement you have. – Debug Diva Feb 20 '22 at 12:04

1 Answers1

0

You added lot of code so I am not able to reproduce with that but as per my understanding you can achieve it by just iterating the input array.

const poems = [
    {
    id: 1,
    name: 'obj1'
  },
  {
    id: 2,
    name: 'obj2'  
  },
  {
    id: 3,
    name: 'obj3'
  },
  {
    id: 4,
    name: 'obj4'
  }
];

let obj = {};

poems.forEach((item) => {
    obj[item.id] = item;
});

console.log(obj);
Debug Diva
  • 26,058
  • 13
  • 70
  • 123