Counting the occurrences / frequency of array elements

前端 未结 30 2061
甜味超标
甜味超标 2020-11-21 06:47

In Javascript, I\'m trying to take an initial array of number values and count the elements inside it. Ideally, the result would be two new arrays, the first specifying each

相关标签:
30条回答
  • 2020-11-21 07:37

    If you favour a single liner.

    arr.reduce(function(countMap, word) {countMap[word] = ++countMap[word] || 1;return countMap}, {});

    Edit (6/12/2015): The Explanation from the inside out. countMap is a map that maps a word with its frequency, which we can see the anonymous function. What reduce does is apply the function with arguments as all the array elements and countMap being passed as the return value of the last function call. The last parameter ({}) is the default value of countMap for the first function call.

    0 讨论(0)
  • 2020-11-21 07:37

    You can simplify this a bit by extending your arrays with a count function. It works similar to Ruby’s Array#count, if you’re familiar with it.

    Array.prototype.count = function(obj){
      var count = this.length;
      if(typeof(obj) !== "undefined"){
        var array = this.slice(0), count = 0; // clone array and reset count
        for(i = 0; i < array.length; i++){
          if(array[i] == obj){ count++ }
        }
      }
      return count;
    }
    

    Usage:

    let array = ['a', 'b', 'd', 'a', 'c'];
    array.count('a'); // => 2
    array.count('b'); // => 1
    array.count('e'); // => 0
    array.count(); // => 5
    

    Gist


    Edit

    You can then get your first array, with each occurred item, using Array#filter:

    let occurred = [];
    array.filter(function(item) {
      if (!occurred.includes(item)) {
        occurred.push(item);
        return true;
      }
    }); // => ["a", "b", "d", "c"]
    

    And your second array, with the number of occurrences, using Array#count into Array#map:

    occurred.map(array.count.bind(array)); // => [2, 1, 1, 1]
    

    Alternatively, if order is irrelevant, you can just return it as a key-value pair:

    let occurrences = {}
    occurred.forEach(function(item) { occurrences[item] = array.count(item) });
    occurences; // => {2: 5, 4: 1, 5: 3, 9: 1}
    
    0 讨论(0)
  • 2020-11-21 07:38

    Based on answer of @adamse and @pmandell (which I upvote), in ES6 you can do it in one line:

    • 2017 edit: I use || to reduce code size and make it more readable.

    var a=[7,1,7,2,2,7,3,3,3,7,,7,7,7];
    alert(JSON.stringify(
    
    a.reduce((r,k)=>{r[k]=1+r[k]||1;return r},{})
    
    ));


    It can be used to count characters:

    var s="ABRACADABRA";
    alert(JSON.stringify(
    
    s.split('').reduce((a, c)=>{a[c]++?0:a[c]=1;return a},{})
    
    ));

    0 讨论(0)
  • 2020-11-21 07:39

    ES6 version should be much simplifier (another one line solution)

    let arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
    let acc = arr.reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());
    
    console.log(acc);
    // output: Map { 5 => 3, 2 => 5, 9 => 1, 4 => 1 }
    

    A Map instead of plain Object helping us to distinguish different type of elements, or else all counting are base on strings

    0 讨论(0)
  • 2020-11-21 07:39

    So here's how I'd do it with some of the newest javascript features:

    First, reduce the array to a Map of the counts:

    let countMap = array.reduce(
      (map, value) => {map.set(value, (map.get(value) || 0) + 1); return map}, 
      new Map()
    )
    

    By using a Map, your starting array can contain any type of object, and the counts will be correct. Without a Map, some types of objects will give you strange counts. See the Map docs for more info on the differences.

    This could also be done with an object if all your values are symbols, numbers, or strings:

    let countObject = array.reduce(
      (map, value) => { map[value] = (map[value] || 0) + 1; return map },
      {}
    )
    

    Or slightly fancier in a functional way without mutation, using destructuring and object spread syntax:

    let countObject = array.reduce(
      (value, {[value]: count = 0, ...rest}) => ({ [value]: count + 1, ...rest }),
      {}
    )
    

    At this point, you can use the Map or object for your counts (and the map is directly iterable, unlike an object), or convert it to two arrays.

    For the Map:

    countMap.forEach((count, value) => console.log(`value: ${value}, count: ${count}`)
    
    let values = countMap.keys()
    let counts = countMap.values()
    

    Or for the object:

    Object
      .entries(countObject) // convert to array of [key, valueAtKey] pairs
      .forEach(([value, count]) => console.log(`value: ${value}, count: ${count}`)
    
    let values = Object.keys(countObject)
    let counts = Object.values(countObject)
    
    0 讨论(0)
  • 2020-11-21 07:39

    Here is a way to count occurrences inside an array of objects. It also places the first array's contents inside a new array to sort the values so that the order in the original array is not disrupted. Then a recursive function is used to go through each element and count the quantity property of each object inside the array.

    var big_array = [
      { name: "Pineapples", quantity: 3 },
      { name: "Pineapples", quantity: 1 },
      { name: "Bananas", quantity: 1 },
      { name: "Limes", quantity: 1 },
      { name: "Bananas", quantity: 1 },
      { name: "Pineapples", quantity: 2 },
      { name: "Pineapples", quantity: 1 },
      { name: "Bananas", quantity: 1 },
      { name: "Bananas", quantity: 1 },
      { name: "Bananas", quantity: 5 },
      { name: "Coconuts", quantity: 1 },
      { name: "Lemons", quantity: 2 },
      { name: "Oranges", quantity: 1 },
      { name: "Lemons", quantity: 1 },
      { name: "Limes", quantity: 1 },
      { name: "Grapefruit", quantity: 1 },
      { name: "Coconuts", quantity: 5 },
      { name: "Oranges", quantity: 6 }
    ];
    
    function countThem() {
      var names_array = [];
      for (var i = 0; i < big_array.length; i++) {
        names_array.push( Object.assign({}, big_array[i]) );
      }
    
      function outerHolder(item_array) {
        if (item_array.length > 0) {
          var occurrences = [];
          var counter = 0;
          var bgarlen = item_array.length;
          item_array.sort(function(a, b) { return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0); });
    
          function recursiveCounter() {
            occurrences.push(item_array[0]);
            item_array.splice(0, 1);
            var last_occurrence_element = occurrences.length - 1;
            var last_occurrence_entry = occurrences[last_occurrence_element].name;
            var occur_counter = 0;
            var quantity_counter = 0;
            for (var i = 0; i < occurrences.length; i++) {
              if (occurrences[i].name === last_occurrence_entry) {
                occur_counter = occur_counter + 1;
                if (occur_counter === 1) {
                  quantity_counter = occurrences[i].quantity;
                } else {
                  quantity_counter = quantity_counter + occurrences[i].quantity;
                }
              }
            }
    
            if (occur_counter > 1) {
              var current_match = occurrences.length - 2;
              occurrences[current_match].quantity = quantity_counter;
              occurrences.splice(last_occurrence_element, 1);
            }
    
            counter = counter + 1;
    
            if (counter < bgarlen) {
              recursiveCounter();
            }
          }
    
          recursiveCounter();
    
          return occurrences;
        }
      }
      alert(JSON.stringify(outerHolder(names_array)));
    }
    
    0 讨论(0)
提交回复
热议问题