Home About Contact
JavaScript

ポケモンリストからタイプ別ごとのネストしたリストを生成する関数を考える

たとえば、次のようなポケモンオブジェクトのリストがあったとして:

[
  { name: 'Eevee', type: 'normal' },
  { name: 'Pidgeot', type: 'normal' },
  { name: 'Pikachu', type: 'electric' },
  { name: 'Raichu', type: 'electric' },
  { name: 'Jigglypuff', type: 'normal' },
  { name: 'Squirtle', type: 'water' },
  { name: 'Golduck', type: 'water' },
  { name: 'Voltorb', type: 'electric' }
]

これをタイプ別にグループ化したリストのリストをつくる関数を考えます。

まず、ポケモンオブジェクトをつくる関数:

const toPokemon = (name, type)=> {
    return {
        name: name,
        type: type};
};

この関数を使って、実験対象となるポケモンリスト生成:

const pokemonlist = [
    toPokemon('Eevee',      'normal'),
    toPokemon('Pidgeot',    'normal'),
    toPokemon('Pikachu',    'electric'),
    toPokemon('Raichu',     'electric'),
    toPokemon('Jigglypuff', 'normal'),
    toPokemon('Squirtle',   'water'),
    toPokemon('Golduck',    'water'),
    toPokemon('Voltorb',    'electric')];

console.log(pokemonlist);

このポケモンリストを先頭から順番に調べて、タイプが同じだったらグループ化する再帰関数を作成:

const toNestedList = (pokemons, acc)=>{
    if(pokemons.length<1){
        return acc;
    }
    else {
        const pokemon = head(pokemons);
        const tailPokemons = tail(pokemons);

        const nestedPokemons = last(acc);
        if( nestedPokemons==null ){
            acc.push([pokemon]);
        }
        else {
            if( nestedPokemons[0].type == pokemon.type ){
                nestedPokemons.push(pokemon);
            }
            else {
                acc.push([pokemon]);
            }
        }

        return toNestedList(tailPokemons, acc);
    }
};

この toNestedList で使用する補助関数 head, tail, last 関数を作成:

const head = (list)=>{
    if(list.length<1){
        return null;
    }
    else {
        return list[0];
    }
};

const tail = (list)=>{
    return list.slice(1);
};

/*
const tail = (list)=>{
    if(list.length<2){
        return [];
    }
    else {
        const newList = [];
        for(var i=1; i<list.length; i++){
            newList.push(list[i]);
        }
        return newList;
    }
};
*/

const last = (list)=>{
    if(list.length<1){
        return null;
    }
    else {
        return list[list.length-1];
    }
};

toNestedList を使ってタイプ別のリストのリストを作成:

const pokemonListListByType = toNestedList(pokemonlist, []);
console.log(pokemonListListByType);

node pokemon.js として実行してみます。

[
  [
    { name: 'Eevee', type: 'normal' },
    { name: 'Pidgeot', type: 'normal' }
  ],
  [
    { name: 'Pikachu', type: 'electric' },
    { name: 'Raichu', type: 'electric' }
  ],
  [ { name: 'Jigglypuff', type: 'normal' } ],
  [
    { name: 'Squirtle', type: 'water' },
    { name: 'Golduck', type: 'water' }
  ],
  [ { name: 'Voltorb', type: 'electric' } ]
]

おっと、それなりにはグループ化できたのですが・・・ 順番にポケモンタイプを調べてサブグループ(ネストされたリスト)をつくるので、 toNestedList に与えるポケモンリストは、事前にタイプ別にソートされている必要がありました。

タイプ別にソートする関数 sortByType を作成:

const sortByType = (pokemons)=>{
    return pokemons.sort( (pokemon0, pokemon1)=>{
        return pokemon0.type.localeCompare(pokemon1.type);
    });
};

localeCompare() とは?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare

これを使って、再度出力してみます。

const pokemonListListByType = toNestedList( sortByType(pokemonlist), []);
console.log(pokemonListListByType);

node pokemon.js として実行:

[
  [
    { name: 'Pikachu', type: 'electric' },
    { name: 'Raichu', type: 'electric' },
    { name: 'Voltorb', type: 'electric' }
  ],
  [
    { name: 'Eevee', type: 'normal' },
    { name: 'Pidgeot', type: 'normal' },
    { name: 'Jigglypuff', type: 'normal' }
  ],
  [
    { name: 'Squirtle', type: 'water' },
    { name: 'Golduck', type: 'water' }
  ]
]

うまくいきました。

まとめ

pokemon.js:

const toPokemon = (name, type)=> {
    return {
        name: name,
        type: type};
};

const pokemonlist = [
    toPokemon('Eevee',      'normal'),
    toPokemon('Pidgeot',    'normal'),
    toPokemon('Pikachu',    'electric'),
    toPokemon('Raichu',     'electric'),
    toPokemon('Jigglypuff', 'normal'),
    toPokemon('Squirtle',   'water'),
    toPokemon('Golduck',    'water'),
    toPokemon('Voltorb',    'electric')];

console.log(pokemonlist);


const head = (list)=>{
    if(list.length<1){
        return null;
    }
    else {
        return list[0];
    }
};

const tail = (list)=>{
    return list.slice(1);
};

const last = (list)=>{
    if(list.length<1){
        return null;
    }
    else {
        return list[list.length-1];
    }
};

const toNestedList = (pokemons, acc)=>{
    if(pokemons.length<1){
        return acc;
    }
    else {
        const pokemon = head(pokemons);
        const tailPokemons = tail(pokemons);

        const nestedPokemons = last(acc);
        if( nestedPokemons==null ){
            acc.push([pokemon]);
        }
        else {
            if( nestedPokemons[0].type == pokemon.type ){
                nestedPokemons.push(pokemon);
            }
            else {
                acc.push([pokemon]);
            }
        }

        return toNestedList(tailPokemons, acc);
    }
};

const sortByType = (pokemons)=>{
    return pokemons.sort( (pokemon0, pokemon1)=>{
        return pokemon0.type.localeCompare(pokemon1.type);
    });
};


//const pokemonListListByType = toNestedList(pokemonlist, []);
const pokemonListListByType = toNestedList( sortByType(pokemonlist), []);
console.log(pokemonListListByType);