1

I'm using the following code (courtesy of JQuery Javascript Sort Array by Highest Count) to sort a list of strings from highest to lowest count:

var items = {}, sortableItems = [], i, len, element, listOfStrings; 

    listOfStrings = JSON.parse(the_chems);


for (i = 0, len = listOfStrings.length; i < len; i += 1) 
    {
        if (items.hasOwnProperty(listOfStrings[i])) {
            items[listOfStrings[i]] += 1;
        } else {
            items[listOfStrings[i]] = 1;
        }
    }

for (element in items) 
    {
        if (items.hasOwnProperty(element)) {
            sortableItems.push([element, items[element]]);
        }
    }

sortableItems.sort(function (first, second) 
    {
        return second[1] - first[1];
    });

Instead of this type of array input

["red", "red", "red", "blue", "blue"]

Which returns

[ [ "red", 3 ], [ "blue", 2 ] ]

I would like to use an array like

[["red","apple"], ["red","chilli"], ["red","melon"], ["blue","ocean"], ["blue","eyes"]]

And return

[["red", 3, ["apple","chilli","melon"]], ["blue", 2, ["blue","ocean"]]
1
  • A note that the sort (provided the count is the 2nd property) can be done by result.sort((count_a, count_b) => count_b[1] - count_a[1]); Commented Aug 22, 2017 at 11:06

6 Answers 6

3

You could use a hash table and collect the result in an array.

var array = [["red", "apple"], ["red", "chilli"], ["red", "melon"], ["blue", "ocean"], ["blue", "eyes"]],
    hash = Object.create(null),
    result = [];

array.forEach(function (a) {
    if (!hash[a[0]]) {
        hash[a[0]] = [a[0], 0, []];
        result.push(hash[a[0]]);
    }
    hash[a[0]][1]++;
    hash[a[0]][2].push(a[1]);
});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

Comments

2

You could use a Map and some cool destructuring:

var hash = new Map(), result = [];

input.forEach(([color,fruit]) => {
 if(hash.has(color)){
  var arr = hash.get(color);
  arr[1]++;
  arr[2].push(fruit);
 }else{
  var tmp = [color,1,[fruit]];
  hash.set(color,tmp);
  result.push(tmp);
 }
});

Try it out (Hint: that would be a good usecase for objects...)


Slower but maybe a bit more readable:

var hash = new Map();

input.forEach(([color,fruit])=>{
 if(hash.has(color)){
  hash.get(color).push(fruit);
 }else{
  hash.set(color,[fruit]);
 }
})

var result = [...hash.entries()].map(([color,fruits])=>[color,fruits.length,fruits]);

Comments

1

Why not create a useful data structure like this:

{
  red:["apple","chili","melon"],
  blue:["ocean","eyes"]
}

Done with one loop and no extraneous hash table:

var arr = [["red","apple"], ["red","chilli"], ["red","melon"], ["blue","ocean"], ["blue","eyes"]];
arr.reduce(function(c,element) {
    if(!c[element[0]]) {
        c[element[0]] = [];
    }
    c[element[0]].push(element[1]);
    return c;
},{});

For those who don't care about readable code:

arr.reduce(function(c,element) {
    return (c[element[0]] ? c[element[0]].push(element[1]) : c[element[0]] = [element[1]]) && c;
},{});

Comments

1

Also possible without map...

function groupAndSort(items){
    var output = []
    for(var i=0;i<items.length;i++){
        var exists = false;
        for(var u=0;u<output.length;u++){
            if(output[u][0]==items[i][0]){
                output[u][1]++
                output[u][2].push(items[i][1])
                exists = true
                break
            }
        }
        if(!exists)
            output.push([items[i][0],1,[items[i][1]]])
    }
    output.sort(function (first, second){
        return second[1] - first[1];
    });
    return output
}

Comments

1

You can use Array.reduce to restructure the array.

const arr = [["red","apple"], ["red","chilli"], ["red","melon"], ["blue","ocean"], ["blue","eyes"]];

const newArr = arr.reduce((acc , curr) => {
  let flag = false;
  acc.forEach(a => {
    if(a[0] === curr[0]) {
      a[1]++;
      a[2].push(curr[1]);
      flag = true;
    }
  });
  if(!flag) {
    let newArr = [curr[0], 1, [curr[1]]];
    acc.push(newArr);
  }
  return acc;
}, []);

console.log(newArr);

Comments

1

Here's another solution. First the function creates a simple count of each color. From there, we go through the object and filter / map through the relevant items, and push our final desired output.

const testArray = [["red","apple"], ["red","chilli"], ["red","melon"], ["blue","ocean"], ["blue","eyes"]]

function modifyArray(arr) {
  let colorCount = arr.reduce((allColors, color) => {
    if (color[0] in allColors) {
      allColors[color[0]]++
	}
	else {
	  allColors[color[0]] = 1
	}
	return allColors
  }, {})

  let newArr = [];

  for (var key in colorCount) {
    let coloredItems = arr.filter(val => val[0] === key).map(val => val[1])
    newArr.push([key, colorCount[key], coloredItems])
  }

  return newArr;
}

console.log(modifyArray(testArray))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.