9

I have a Map() object that I need to iterate, so I can get the day of the week and a selected hour. The code below doesn't work, because Object.keys(newFieldReservationPrice).forEach is trying to loop a Map() object, which seems to make no sense. So, is there a better solution for this?

Here is the code below:

handlePriceInput = (e, hour, day) => {
let value = e.target.value

const newFieldReservationPrice = this.state.newFieldReservationPrice
console.log('newFieldReservationPrice', newFieldReservationPrice) // A Map();
let map;

if (!newFieldReservationPrice instanceof Map) {
  console.log('!== Map')
  console.log('newFieldReservationPrice is a Map()? inside if ()', newFieldReservationPrice)
  if (newFieldReservationPrice[day] && newFieldReservationPrice[day][hour]) {
      newFieldReservationPrice[day][hour] = Number(value)
  } 
} else {
  map = new Map();
  console.log('map object', Object.keys(newFieldReservationPrice)) // logs map object []

  Object.keys(newFieldReservationPrice).forEach(key => {
    console.log('key', key)
      map.set(key, new Map(Object.entries(newFieldReservationPrice[key])));
  }); // This doesn't work
  console.log('Am I a Map()? map', map)

  const aux = map.get(day)
  console.log('aux day', aux) // A Map()
  aux.set(hour, Number(value)) // Comes as undefined || Cannot read property 'set' of undefined
  console.log('aux set', aux) // A Map()

  map.set(day, aux);
  console.log('what do I have?', map)

}
const isReservationPrice = !newFieldReservationPrice instanceof Map ? newFieldReservationPrice : map
console.log('isReservationPrice', isReservationPrice)

this.setState({
  newFieldReservationPrice: isReservationPrice
})
}

Thank you! :)

PrakashG
  • 1,642
  • 5
  • 20
  • 30
RCohen
  • 1,702
  • 10
  • 24
  • 44
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach Search on google a bit before asking question – Vaibhav Vishal Feb 04 '19 at 10:01
  • 1
    Possible duplicate: https://stackoverflow.com/questions/47135661/how-to-get-a-key-in-a-javascript-map-by-its-value/47136047#47136047. Not exact dupe so not marking as dupe though – Rajesh Feb 04 '19 at 10:01
  • 1
    Possible duplicate of [Using map() on an iterator](https://stackoverflow.com/questions/43885365/using-map-on-an-iterator) – adiga Feb 04 '19 at 10:02

3 Answers3

16

Maps provide three ways to get iterators for their contents:

  • keys - Iterates the keys in the map
  • values - Iterates the values
  • entries - Iterates the key and values, giving you [key, value] arrays (this is the default)

As Nina notes, Maps also provide forEach, which loops through their contents giving the callback the value, key, and map as arguments.

Use the one appropriate for your use case. For instance, if you're trying to replace Object.keys, use keys. Similarly, if you want the entries in the map (I notice you use Object.entries at one point), use entries or the default iterator.

Note that in a few places in your code, you seem to be trying to index into the map using []. That doesn't work for maps, you need to use get.

Here's a simple example of using the default iterator (which is also the one you get from entries):

const map = new Map();
map.set(1, "one");   // Could also include these when calling
map.set(2, "two");   // the constructor but I wanted to
map.set(3, "three"); // avoid any confusion

for (const [key, value] of map) { // Using the default iterator (could be `map.entries()` instead)
    console.log(`The value for key ${key} is ${value}`);
}

Also note that you're breaking one of React's rules by setting new state based on existing state, but not using the callback version of setState. If you need to use existing state when setting new state, you must use the callback version:

this.setState(prevState => {
    // ...use `prevState` values (*not* `this.state`!) to create an object with
    // the updates, then return the object...
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thank you for the explanation. You mean something like `map.set(key, new Map(Object.entries(newFieldReservationPrice.get(key))))` ? – RCohen Feb 04 '19 at 10:14
  • @RCohen - What are you trying to do with `new Map(Object.entries(newFieldReservationPrice.get(key)))`? – T.J. Crowder Feb 04 '19 at 10:16
  • Like you said, indexing [key] into the map doesn't work. So the idea here is something like: - monday: - 08:00: 10 (storing 10 in this day and hour) Get the day and the hour, in this case is 08:00 to save a new value. With `newFieldReservationPrice.forEach` I iterate the hours – RCohen Feb 04 '19 at 10:20
  • @RCohen - Yes, but why are you creating a Map from the result? But yes, I mean using `newFieldReservationPrice.get(key)` instead of `newFieldReservationPrice[key]`. It was the *rest* of that that doesn't make sense to me, since it assumes that the values stored in `newFieldReservationPrice` are not maps (because it uses `Object.entries`), but you're setting a Map as a value in `newFieldReservationPrice`. – T.J. Crowder Feb 04 '19 at 10:24
  • This is because this is data that comes from DB as an object and I need to send it back as a Map. That is why I'm doing this. This is a way I found, maybe not the best one. @T.J Crowder – RCohen Feb 04 '19 at 10:26
  • @RCohen - The problem is you'll end up with `newFieldReservationPrice` containing a mix of non-Map objects and Maps. Either convert them all to Maps on receipt before putting them in `newFieldReservationPrice`, or don't convert them to Maps in this operation. – T.J. Crowder Feb 04 '19 at 10:36
  • Yeah, that is what I saw just now. @T.J. Crowder – RCohen Feb 04 '19 at 10:37
11

You can iterate a Map object using for of:

for (const [key, value] of myMap) {
  console.log(key, value);
}

which is the same as iterating over the entries:

for (const [key, value] of myMap.entries()) {
  console.log(key, value);
}

As @Nina Scholz said, you can use forEach on the Map prototype (doc MDN):

myMap.forEach((value, key, map) => {
  console.log(key, value);
}
CruelCow
  • 119
  • 1
  • 6
jo_va
  • 13,504
  • 3
  • 23
  • 47
  • I don't know why, with `Object.entries(newFieldReservationPrice).forEach` doesn't work for me. The same for `Object.keys(newFieldReservationPrice).forEach` – RCohen Feb 04 '19 at 10:09
5

You could use Map#forEach and iterate the Map directly.

Nina Scholz
  • 376,160
  • 25
  • 347
  • 392