Collecto
Collecto

Reputation: 119

Optimizing nested for loop algorithm, Javascript

I am using the pokemon API to build a fun little informational app. I specifically want to have a section detailing the damage relationships of a pokemon. I currently retrieve and format the data as such:

Array [
  Object {
    "double_damage_from": Array [
      Object {
        "name": "flying",
        "url": "https://pokeapi.co/api/v2/type/3/",
      },
      Object {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      Object {
        "name": "bug",
        "url": "https://pokeapi.co/api/v2/type/7/",
      },
      Object {
        "name": "fire",
        "url": "https://pokeapi.co/api/v2/type/10/",
      },
      Object {
        "name": "ice",
        "url": "https://pokeapi.co/api/v2/type/15/",
      },
    ],
    "double_damage_to": Array [
      Object {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      Object {
        "name": "rock",
        "url": "https://pokeapi.co/api/v2/type/6/",
      },
      Object {
        "name": "water",
        "url": "https://pokeapi.co/api/v2/type/11/",
      },
    ],
    "half_damage_from": Array [
      Object {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      Object {
        "name": "water",
        "url": "https://pokeapi.co/api/v2/type/11/",
      },
      Object {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      Object {
        "name": "electric",
        "url": "https://pokeapi.co/api/v2/type/13/",
      },
    ],
    "half_damage_to": Array [
      Object {
        "name": "flying",
        "url": "https://pokeapi.co/api/v2/type/3/",
      },
      Object {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      Object {
        "name": "bug",
        "url": "https://pokeapi.co/api/v2/type/7/",
      },
      Object {
        "name": "steel",
        "url": "https://pokeapi.co/api/v2/type/9/",
      },
      Object {
        "name": "fire",
        "url": "https://pokeapi.co/api/v2/type/10/",
      },
      Object {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      Object {
        "name": "dragon",
        "url": "https://pokeapi.co/api/v2/type/16/",
      },
    ],
    "name": "grass",
    "no_damage_from": Array [],
    "no_damage_to": Array [],
  },
  Object {
    "double_damage_from": Array [
      Object {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      Object {
        "name": "psychic",
        "url": "https://pokeapi.co/api/v2/type/14/",
      },
    ],
    "double_damage_to": Array [
      Object {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      Object {
        "name": "fairy",
        "url": "https://pokeapi.co/api/v2/type/18/",
      },
    ],
    "half_damage_from": Array [
      Object {
        "name": "fighting",
        "url": "https://pokeapi.co/api/v2/type/2/",
      },
      Object {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      Object {
        "name": "bug",
        "url": "https://pokeapi.co/api/v2/type/7/",
      },
      Object {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      Object {
        "name": "fairy",
        "url": "https://pokeapi.co/api/v2/type/18/",
      },
    ],
    "half_damage_to": Array [
      Object {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      Object {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      Object {
        "name": "rock",
        "url": "https://pokeapi.co/api/v2/type/6/",
      },
      Object {
        "name": "ghost",
        "url": "https://pokeapi.co/api/v2/type/8/",
      },
    ],
    "name": "poison",
    "no_damage_from": Array [],
    "no_damage_to": Array [
      Object {
        "name": "steel",
        "url": "https://pokeapi.co/api/v2/type/9/",
      },
    ],
  },
]

What I want to do is format it like this.

DamageMap:  Map {
  "double_damage_from" => Array [
    "flying",
    "poison",
    "bug",
    "fire",
    "ice",
    "ground",
    "psychic",
  ],
  "double_damage_to" => Array [
    "ground",
    "rock",
    "water",
    "grass",
    "fairy",
  ],
  "half_damage_from" => Array [
    "ground",
    "water",
    "grass",
    "electric",
    "fighting",
    "poison",
    "bug",
    "fairy",
  ],
  "half_damage_to" => Array [
    "flying",
    "poison",
    "bug",
    "steel",
    "fire",
    "grass",
    "dragon",
    "ground",
    "rock",
    "ghost",
  ],
  "no_damage_from" => Array [],
  "no_damage_to" => Array [
    "steel",
  ],
}

The data is formatted into this map with no duplicate types. This is my current solution.

const keys = [
      "double_damage_from",
      "double_damage_to",
      "half_damage_from",
      "half_damage_to",
      "no_damage_from",
      "no_damage_to",
    ];
    const damageMap = new Map();

    for (let i = 0; i < results.length; ++i) {
      for (let j = 0; j < keys.length; ++j) {
        if (!damageMap.has(keys[j])) {
          damageMap.set(keys[j], []);
        }
        const val = damageMap.get(keys[j]);
        const curr = results[i][keys[j]];

        for (let k = 0; k < curr.length; ++k) {
          if (val.indexOf(curr[k].name) === -1) {
            val.push(curr[k].name);
          }
        }
        damageMap.set(keys[j], val);
      }
    }

    return damageMap;
  };

It is utterly atrocious... I know this. My problem is that any attempt at optimizing it has so far failed. I have tried using combinations of map and reduce functions to no avail. If anybody can take a look at this and optimize it, it would be greatly appreciated!

Upvotes: 2

Views: 77

Answers (3)

Sascha A.
Sascha A.

Reputation: 4616

Using Array.reduce for collecting the data, Array#forEach to iterate over the elements and Object.entries to get key/values from the object.

let map = arr.reduce((acc, cur) => {
   Object.entries(cur).forEach(([key, values]) => {
       if (!acc[key] && key !=='name') {
           acc[key] = [];
       }
       if (typeof(values)=== 'object') {
           values.forEach(({name}) => {
               acc[key].push(name);
           });
       }
   });
   return acc;
}, {});

Here for playing arround https://jsfiddle.net/h8kL5gp9/ or try this (with the hole code):

let arr = [
   {
    "double_damage_from": [
      {
        "name": "flying",
        "url": "https://pokeapi.co/api/v2/type/3/",
      },
      {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      {
        "name": "bug",
        "url": "https://pokeapi.co/api/v2/type/7/",
      },
      {
        "name": "fire",
        "url": "https://pokeapi.co/api/v2/type/10/",
      },
      {
        "name": "ice",
        "url": "https://pokeapi.co/api/v2/type/15/",
      },
    ],
    "double_damage_to": [
      {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      {
        "name": "rock",
        "url": "https://pokeapi.co/api/v2/type/6/",
      },
      {
        "name": "water",
        "url": "https://pokeapi.co/api/v2/type/11/",
      },
    ],
    "half_damage_from": [
      {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      {
        "name": "water",
        "url": "https://pokeapi.co/api/v2/type/11/",
      },
      {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      {
        "name": "electric",
        "url": "https://pokeapi.co/api/v2/type/13/",
      },
    ],
    "half_damage_to": [
      {
        "name": "flying",
        "url": "https://pokeapi.co/api/v2/type/3/",
      },
      {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      {
        "name": "bug",
        "url": "https://pokeapi.co/api/v2/type/7/",
      },
      {
        "name": "steel",
        "url": "https://pokeapi.co/api/v2/type/9/",
      },
      {
        "name": "fire",
        "url": "https://pokeapi.co/api/v2/type/10/",
      },
      {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      {
        "name": "dragon",
        "url": "https://pokeapi.co/api/v2/type/16/",
      },
    ],
    "name": "grass",
    "no_damage_from": [],
    "no_damage_to": [],
  },
  {
    "double_damage_from": [
      {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      {
        "name": "psychic",
        "url": "https://pokeapi.co/api/v2/type/14/",
      },
    ],
    "double_damage_to": [
      {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      {
        "name": "fairy",
        "url": "https://pokeapi.co/api/v2/type/18/",
      },
    ],
    "half_damage_from": [
      {
        "name": "fighting",
        "url": "https://pokeapi.co/api/v2/type/2/",
      },
      {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      {
        "name": "bug",
        "url": "https://pokeapi.co/api/v2/type/7/",
      },
      {
        "name": "grass",
        "url": "https://pokeapi.co/api/v2/type/12/",
      },
      {
        "name": "fairy",
        "url": "https://pokeapi.co/api/v2/type/18/",
      },
    ],
    "half_damage_to": [
      {
        "name": "poison",
        "url": "https://pokeapi.co/api/v2/type/4/",
      },
      {
        "name": "ground",
        "url": "https://pokeapi.co/api/v2/type/5/",
      },
      {
        "name": "rock",
        "url": "https://pokeapi.co/api/v2/type/6/",
      },
      {
        "name": "ghost",
        "url": "https://pokeapi.co/api/v2/type/8/",
      },
    ],
    "name": "poison",
    "no_damage_from": [],
    "no_damage_to": [
      {
        "name": "steel",
        "url": "https://pokeapi.co/api/v2/type/9/",
      },
    ],
  },
];

let map = arr.reduce((acc, cur) => {
   Object.entries(cur).forEach(([key, values]) => {
       if (!acc[key] && key !=='name') {
           acc[key] = [];
       }
       if (typeof(values)=== 'object') {
           values.forEach(({name}) => {
               acc[key].push(name);
           });
       }
   });
   return acc;
}, {});

console.log(map);

Upvotes: 0

Yevhen Horbunkov
Yevhen Horbunkov

Reputation: 15530

You may use Array.prototype.reduce() together with Array.prototype.forEach() instead:

const src = [{"double_damage_from":[{"name":"flying","url":"https://pokeapi.co/api/v2/type/3/",},{"name":"poison","url":"https://pokeapi.co/api/v2/type/4/",},{"name":"bug","url":"https://pokeapi.co/api/v2/type/7/",},{"name":"fire","url":"https://pokeapi.co/api/v2/type/10/",},{"name":"ice","url":"https://pokeapi.co/api/v2/type/15/",},],"double_damage_to":[{"name":"ground","url":"https://pokeapi.co/api/v2/type/5/",},{"name":"rock","url":"https://pokeapi.co/api/v2/type/6/",},{"name":"water","url":"https://pokeapi.co/api/v2/type/11/",},],"half_damage_from":[{"name":"ground","url":"https://pokeapi.co/api/v2/type/5/",},{"name":"water","url":"https://pokeapi.co/api/v2/type/11/",},{"name":"grass","url":"https://pokeapi.co/api/v2/type/12/",},{"name":"electric","url":"https://pokeapi.co/api/v2/type/13/",},],"half_damage_to":[{"name":"flying","url":"https://pokeapi.co/api/v2/type/3/",},{"name":"poison","url":"https://pokeapi.co/api/v2/type/4/",},{"name":"bug","url":"https://pokeapi.co/api/v2/type/7/",},{"name":"steel","url":"https://pokeapi.co/api/v2/type/9/",},{"name":"fire","url":"https://pokeapi.co/api/v2/type/10/",},{"name":"grass","url":"https://pokeapi.co/api/v2/type/12/",},{"name":"dragon","url":"https://pokeapi.co/api/v2/type/16/",},],"name":"grass","no_damage_from":[],"no_damage_to":[],},{"double_damage_from":[{"name":"ground","url":"https://pokeapi.co/api/v2/type/5/",},{"name":"psychic","url":"https://pokeapi.co/api/v2/type/14/",},],"double_damage_to":[{"name":"grass","url":"https://pokeapi.co/api/v2/type/12/",},{"name":"fairy","url":"https://pokeapi.co/api/v2/type/18/",},],"half_damage_from":[{"name":"fighting","url":"https://pokeapi.co/api/v2/type/2/",},{"name":"poison","url":"https://pokeapi.co/api/v2/type/4/",},{"name":"bug","url":"https://pokeapi.co/api/v2/type/7/",},{"name":"grass","url":"https://pokeapi.co/api/v2/type/12/",},{"name":"fairy","url":"https://pokeapi.co/api/v2/type/18/",},],"half_damage_to":[{"name":"poison","url":"https://pokeapi.co/api/v2/type/4/",},{"name":"ground","url":"https://pokeapi.co/api/v2/type/5/",},{"name":"rock","url":"https://pokeapi.co/api/v2/type/6/",},{"name":"ghost","url":"https://pokeapi.co/api/v2/type/8/",},],"name":"poison","no_damage_from":[],"no_damage_to":[{"name":"steel","url":"https://pokeapi.co/api/v2/type/9/",},],},],


    result = src.reduce((acc, o) => {
      Object.keys(o).forEach(key => {
        if(Array.isArray(o[key])){
          const match = acc.get(key),
                items = o[key].map(({name}) => name)
          match ? 
          match.push(...items) :
          acc.set(key, items)
        }
       }) 
      return acc
    }, new Map)
    
console.log(result)

Upvotes: 3

mr rogers
mr rogers

Reputation: 3260

Does this work out?

I didn't know what to call each subelement from the main values array so thing is what i used. But this just digs into there and pulls out the name from each of the sublevel elements and tallies them up.

I didn't worry about uniqueness but you could (after concat) add a uniq clean up thing.

// where `values` is your initial data structure

const damageMap = values.reduce((memo, thing) => {
  Object.keys(thing).forEach(key => {
    if (key !== 'name') {
      memo[key] = memo[key] || []
      memo[key] = memo[key].concat(
        thing[key].map(({name}) => name))
    }
  })
  return memo
},{});

Upvotes: 0

Related Questions