dizzy
dizzy

Reputation: 1277

Javascript Map a Collection

The Issue:

I'm attempting to build a simple search tool. It returns a search query by matching an id to another item with the same id. Without going into the complexities, the issue I'm having is that when my data was organized previously, the map function from javascript returned all the results perfectly. However, now that my data is structured a bit differently (a collection, I think?) ....the ids don't appear to be lining up which causes the wrong search results to show.

The function in question:

const options = this.props.itemIds.map((id) => (
  <Option key={this.props.itemSearchList[id].id}>
     {this.props.itemSearchList[id].name}
  </Option>
));

When the data was structured like this it worked as expected:

Example of previous structure:

const items = [
  {
    id: 0,
    name: "name 0",
    tags: ['#sports', '#outdoor', '#clothing'],
  },
  {
    id: 1,
    name: "name 1",
    tags: ['#sports', '#outdoor', '#clothing'],
  },
  {
    id: 2,
    name: "Name 2",
    tags: ['#sports', '#outdoor', '#clothing'],
  },

Now that the data is a ?collection...the map function doesn't work as anticipated and it returns improper results or none at all: I've been able to use the lodash Map function on this structure successfully in the past.

Here's a screenshot of the new data:

enter image description here

I believe a representative way to write out the example would be:

const newItems = [
  0: {
    id: 0,
    name: "name here",
  },
  1: {
    id: 1,
    name: "name here",
  },
]

Any recommendations for making this work or need more info? Perhaps I'm misunderstanding the issue entirely, but I believe it has to do with data structure and the map function from JS. I can see results returning, but the id's are not lining up appropriately anymore.

Here's a visual representation of the misalignment. The orange is the search input and it pulling the right result. The green is the misalignment of what it's actually showing because of the data structure and mapping (I assume).

enter image description here

Upvotes: 2

Views: 2020

Answers (3)

dizzy
dizzy

Reputation: 1277

Here was my simple solution:

const options = [];
    this.props.itemList.forEach((item) => {
      if (this.props.searchResults.includes(item.id)) {
        options.push(<Option key={item.id}>{item.name}</Option>);
      }
    });

Let me know what you think (to the group that tried to help!)

Upvotes: 0

SamVK
SamVK

Reputation: 3435

The issue is you were using index and lining that up with id as a sort of pseudo-key which is...beyond fragile. What you should be doing is keying by id (meaing itemsshould be an object) and then having a seperate array that stores the order you want. So items would be an object keyed by id:

const items = {
  1: {
    id: 1,
    name: "name 1",
    tags: ['#sports', '#outdoor', '#clothing'],
  },
  2: {
    id: 2,
    name: "name 2",
    tags: ['#sports', '#outdoor', '#clothing'],
  },
  9: {
    id: 9,
    name: "Name 9",
    tags: ['#sports', '#outdoor', '#clothing'],
  },
};

And then itemIds (which it appears you already have) is an array with the correct order:

const itemIds = [1,9,2];

And then they can be accessed in the right order by looping over that array, and getting the element by said key:

itemIds.map((id) => {
  const item = items[id];
  // do something with the item
}

Take a look at how Redux recommends normalizing state shape.

https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape

Upvotes: 2

Jonas Wilms
Jonas Wilms

Reputation: 138457

What you call "collections" and "maps" are actually arrays. Now one of the arrays has the objects exactly at the position in the array that matches the id:

items[5].id === 5

Now through sorting /mutating / whatever you change the order so that the element at a certain position doesnt have that as an id:

newItems[5].id // 7 :(

That means that you cannot access the item that easy anymore, you now either have to sort the array again to bring it into order, or you search for an object with the id:

newItems.find(item => item.id === 5) // { id: 5, ... }

Or you switch over to some unsorted collections like a real Map:

const itemsMap = new Map(newItems.map(item => ([item.id, item])));

So you can get a certain item with its id as:

itemsMap.get(5) // { id: 5, ... }

... but the whole thing doesnt have to do with Array.prototype.map at all.

Upvotes: 1

Related Questions