0

I want to create an array 2D that contains each alphabet. The problem is, I am just able to create an array 2D that contains all alphabet. I do the following.

function groupAnimals(animals) {

  var sort = []
  var alphabet = 'abcdefghijklmnopqrstuvwxyz'
  var temp = []
  for(var i = 0; i < alphabet.length; i++){
    for(var j = 0; j < animals.length; j++){
      if(animals[j][0] == alphabet[i]){
        temp.push(animals[j])
      } 
  }

}
sort.push(temp)
return sort
}


console.log(groupAnimals(['bear', 'chicken', 'dolphin', 'cat', 'tiger']));
console.log(groupAnimals(['elephant', 'fish', 'horse', 'bird', 'flamingo', 'dog', 'ant' ]));

but the output is

[ [ 'bear', 'chicken', 'cat', 'dolphin', 'tiger' ] ]
[ [ 'ant', 'bird', 'dog', 'elephant', 'fish', 'flamingo', 'horse' ] ]

instead of

[ ['bear'], ['chicken', 'cat], ['dolphin'], ['tiger'] ]
[ ['ant'], ['bird'], ['dog'], ['elephant'], ['fish', 'flamingo'], ['horse']]

i have tried to make an array temp first after alphabet looping, and then push the animal's name to it, but it's too manual, draining time and there will be an empty array if there's no such character. I want to do it by looping, but i have no idea how to do the loop.

function groupAnimals(animals) {

  var sort = []
  var alphabet = 'abcdefghijklmnopqrstuvwxyz'
  for(var i = 0; i < alphabet.length; i++){
    var A = []
    var B = []
    var C = []
    var D = []
    var E = []
    var F = []
    var T = []
    for(var j = 0; j < animals.length; j++){
      if(animals[j][0] == 'a'){
        A.push(animals[j])
      } else if(animals[j][0] == 'b'){
        B.push(animals[j])
      } else if(animals[j][0] == 'c'){
        C.push(animals[j])
      } else if(animals[j][0] == 'd'){
        D.push(animals[j])
      } else if(animals[j][0] == 'e'){
        E.push(animals[j])
      } else if(animals[j][0] == 'f'){
        F.push(animals[j])
      } else if(animals[j][0] == 't'){
        T.push(animals[j])
      }
  }

}
sort.push(A)
sort.push(B)
sort.push(C)
sort.push(D)
sort.push(E)
sort.push(F)
sort.push(T)
return sort
}

and the result is

[ [],
  [ 'bear' ],
  [ 'chicken', 'cat' ],
  [ 'dolphin' ],
  [],
  [],
  [ 'tiger' ] ]
[ [ 'ant' ],
  [ 'bird' ],
  [],
  [ 'dog' ],
  [ 'elephant' ],
  [ 'fish', 'flamingo' ],
  [] ]

use array and loops are preferable

ulfa
  • 17
  • 6

2 Answers2

2

Reduce into an object of arrays, where the keys are the first letter of the words in the value arrays, then use Object.values to transform it back into an array of arrays:

const groupAnimals = (animals) => {
  const sorted = animals.slice().sort();
  const groupedObj = sorted.reduce((a, word) => {
    const key = word[0];
    if (!a[key]) {
      a[key] = [];
    }
    a[key].push(word);
    return a;
  }, {});
  return Object.values(groupedObj);
};

console.log(groupAnimals(['bear', 'chicken', 'dolphin', 'cat', 'tiger']));
console.log(groupAnimals(['elephant', 'fish', 'horse', 'bird', 'flamingo', 'dog', 'ant']));

The above will work, but if you're worried about the specification not officially guaranteeing property order, you can sort the array of arrays afterwards:

const groupAnimals = (animals) => {
  const groupedObj = animals.reduce((a, word) => {
    const key = word[0];
    if (!a[key]) {
      a[key] = [];
    }
    a[key].push(word);
    return a;
  }, {});
  return Object.values(groupedObj)
    .sort((a, b) => a[0][0].localeCompare(b[0][0]));
};

console.log(groupAnimals(['bear', 'chicken', 'dolphin', 'cat', 'tiger']));
console.log(groupAnimals(['elephant', 'fish', 'horse', 'bird', 'flamingo', 'dog', 'ant']));
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Thank you for the answer! But can't it be done without 'const' ? how about if I only use 'var'? – ulfa Sep 15 '19 at 03:28
  • `const` is *definitely* preferable, but if you don't want to use it, you can replace it with `var` – CertainPerformance Sep 15 '19 at 03:29
  • shouldn't it be sorted after `Object.values()` instead of initially sorting and then proceeding ? – Code Maniac Sep 15 '19 at 03:32
  • @CodeManiac You could do that too, but it doesn't really matter – CertainPerformance Sep 15 '19 at 03:33
  • @CertainPerformance but object doesn't guaranty order right ? – Code Maniac Sep 15 '19 at 03:34
  • @CodeManiac The specification doesn't say so, but the property order is still predictable in all implementations – CertainPerformance Sep 15 '19 at 03:35
  • @CertainPerformance i was referring to this [`answer`](https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties), – Code Maniac Sep 15 '19 at 03:49
  • 1
    @CodeManiac Yep, I know about it, but despite the fact that the specification doesn't specify property order, in every implementation that you'll find, property order *will* be the same anyway (which is why there is currently a proposal to officialize it - it's already implemented everywhere) – CertainPerformance Sep 15 '19 at 04:02
  • @CertainPerformance oh yes i agree, i hopping one day community will agree and use same standard thorughout all the implementations so we need not to scratch head around whether it support particular browser or not, – Code Maniac Sep 15 '19 at 04:05
  • Could I it be done only use array or loops without object ? – ulfa Sep 15 '19 at 04:25
  • @ulfa I suppose it would be *possible*, but it would be a whole lot less elegant, and be more computationally expensive (by an order of magnitude). Using an object (or something like it) to group things **is** the best choice here, by far – CertainPerformance Sep 15 '19 at 04:27
0

Squished CertainPerformance's answer and changed everything to lower case first incase it affects sorting.

function groupStrings(strings) {
    return Object.values(strings.sort().reduce((a, str) => {
        const s = str.toLowerCase();
        const f = s[0];
        a[f] = a[f] ? [...a[f], s] : [s];
        return a;
    }, {}));
}

console.log(groupStrings(['bear', 'chicken', 'dolphin', 'cat', 'tiger']));
console.log(groupStrings(['chevrolet', 'buick', 'dodge', 'bmw', 'mercedes', 'jaguar', 'landrover', 'audi', 'volkswagen', 'cadilac', 'ford', 'toyota', 'tesla']));

Edit

Here are a few ways to do with arrays and for loops only. Explanations are in the comments:

function groupStrings(strings) {

    // first sort the array
    var sortedStrings = strings.slice().sort();

    // create the start of the 2d array with the first word
    var twoDArray = [[sortedStrings[0]]];

    for (var i = 1; i < sortedStrings.length; i++) {

        // get the last child array in the 2d array
        var currentArray = twoDArray[twoDArray.length-1];

        // if the first letter of the current string is equal to the first letter of a string in the current child array,
        // add the current string to the current child array
        if (sortedStrings[i][0] === currentArray[0][0]) {
            currentArray.push(sortedStrings[i]);
            currentArray = currentArray.sort();

        // otherwise create a new child array with the current string
        } else {
            twoDArray.push([sortedStrings[i]])
        }

    }
    
    return twoDArray;
};

console.log(groupStrings(['bear', 'chicken', 'dolphin', 'cat', 'tiger']));
console.log(groupStrings(['chevrolet', 'buick', 'dodge', 'bmw', 'mercedes', 'jaguar', 'landrover', 'audi', 'volkswagen', 'cadilac', 'ford', 'toyota', 'tesla']));

function groupStrings(strings) {

    var alphabet = 'abcdefghijklmnopqrstuvwxyz';
    var twoDArray = [];

    for (var i = 0; i < alphabet.length; i++) {

        // create a temporary array for all words starting with the current letter
        var letterArray = [];

        for (var j = 0; j < strings.length; j++) {

            // if the first letter of the current string is equal to the current letter,
            // add it to the letter array
            if (strings[j][0] === alphabet[i]) {
                letterArray.push(strings[j]);
            }
        }

        // if any strings were added, add the letter array to the 2d array
        if (letterArray.length) {
            twoDArray.push(letterArray);
        }
    }

    return twoDArray;
};

console.log(groupStrings(['bear', 'chicken', 'dolphin', 'cat', 'tiger']));
console.log(groupStrings(['chevrolet', 'buick', 'dodge', 'bmw', 'mercedes', 'jaguar', 'landrover', 'audi', 'volkswagen', 'cadilac', 'ford', 'toyota', 'tesla']));
RyanDay
  • 1,856
  • 16
  • 23