0

Here is my code:

  function createMatchList(data) {
    var matchArray = [];
      for (var i = 0; i < initialData.length; i++) {
        var listData = cleanData(initialData[i][data]);
        if (matchArray.length) {
          for (var a = 0; a < matchArray.length; a++) {
            if (matchArray[a][data] == listData) {
              matchArray[a].Count = matchArray[a].Count + 1;
            } else {
              // THIS CAUSES LOOP
              matchArray.push({ [data]: listData, "Count": 1 });
            }
          }
        } else {
          matchArray.push({ [data]: listData, "Count": 1 });
        }
      }
    }

Essentially, this appears to work outside of when a matching object isn't found, I have an else to push that object to the array and it causes a loop.

I'm not sure why this would happen and I'm totally lost..

I have an initial if/else to see if the array is empty, if it is empty, push the initial object into the array, past this point if the array isn't empty, I check the array using a for loop and the if statement works, but my else statement causes the loop. If I were to take that matchArray.push({ [data]: listData, "Count": 1 }); out of the else statement and just console.log something, it would successfully log for each additional iteration in the first for loop for the initialData.

And just to give an idea of what the array looks like with an object in it:

[
   {
      Date: "27 June 1911", 
      Count: 1
   }
]

My goal is to search the Array for matching Dates and if there is a match, don't push to the Array but instead update the Count. The reason for this is I have a huge raw JSON file with duplicates and I'm trying to create a new data source removing duplicates but counting the occurrences.

Thanks

dbrree
  • 613
  • 7
  • 23
  • 1
    use Array.filter() – GifCo May 09 '19 at 18:29
  • show desired input and output – Dennis Vash May 09 '19 at 18:31
  • @DennisVash, the input is just a string with the raw date like 27 June 1911. The output would be what is shown above with the Date and Count in the object. the 'data' in the function is 'Date'. – dbrree May 09 '19 at 18:37
  • @melpomene sorry, I had typoed. Fixed the code. I had a '(' in the wrong spot in the example above, but not in my actual code. – dbrree May 09 '19 at 18:38
  • `ReferenceError: initialData is not defined` – melpomene May 09 '19 at 18:42
  • @melpomene yes... because that data is coming from elsewhere not included in this code snippet. I explained the issue and where I'm running into the loop. Do I need to mock this up so it works for you? – dbrree May 09 '19 at 18:45
  • 1
    Yes, of course. See [mcve] (and https://ericlippert.com/2014/03/05/how-to-debug-small-programs/ while you're at it). – melpomene May 09 '19 at 18:46

2 Answers2

2

Use Set and Array.filter():

[...new Set(dates)].map(date => ({
  date,
  count: dates.filter(currDate => date === currDate).length
}));

Note: By convention object keys should be lowercased, date and not Date.

Or if you feel brave:

const counts = {};
dates.forEach(date => (counts[date] = (counts[date] || 0) + 1));

const dates = [
  '27 June 1911',
  '27 June 1952',
  '27 March 1911',
  '27 June 1952',
  '24 June 1911',
  '27 June 1952'
];

const datesCount = [...new Set(dates)].map(date => ({
  date,
  count: dates.filter(currDate => date === currDate).length
}));

console.log('datesCount', datesCount);


const counts = {};
dates.forEach(date => (counts[date] = (counts[date] || 0) + 1));

const dateCount2 = Object.entries(counts).map(([date, count]) => ({
  date,
  count
}));

console.log('dateCount2', dateCount2);

// Make unique array of dates.
const datesSet = [...new Set(dates)];

// Count by filtering the array and getting it's length.
const datesCount = datesSet.map(date => ({
  date,
  count: dates.filter(currDate => date === currDate).length
}));
Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
  • Quick question for you if you don't mind. In this `const matchCount = [...new Set(matchArray)].map(data => ({ data, count: matchArray.filter(currData => data === currData).length }));` What if I want the 'data' object to be named based on a value in another variable? So in my code example, you will see where I'm bring in 'data' in the function. This could be, Data, Place, Time, etc... how could I make that object Dynamic? I assumed it would be [data] but that doesn't appear to work. Thanks again. – dbrree May 09 '19 at 19:33
  • 1
    `{ [myKey]: 'hello' };` should work or `myObj[myKey] = 'hello'`, consider asking a new question, you can send a link to me and I'll help. – Dennis Vash May 09 '19 at 19:38
  • No worries, this is what I was doing, so I will just mess with it a bit. Not sure why it isn't working, but thank you! – dbrree May 09 '19 at 19:47
1

How important is performance and are you using >= 2015? If so, you could just map instead and reduce some code complexity like this answer:

const counts = new Map([...new Set(a)].map(
    x => [x, a.filter(y => y === x).length]
));

Then to get the count you would just call:

counts.get(x)

In the case where you need to store it in an object that looks exactly as you've outlined above, I would do something like:

let unique = Set(dates);
var matchArray = [];

unique.forEach(function (date) {
    matchArray.push({ 
        "Date": date, 
        "Count": dates.filter(x => x === date).length 
    });
});
Ashley
  • 897
  • 1
  • 5
  • 17
  • Hey Ashley, thanks for the reply. I still need to store the counts in the object for reporting purposes to essentially say, on 'x' date, we had 'y' occurrences. – dbrree May 09 '19 at 18:41
  • Edited my example, but didn't test. Good luck! – Ashley May 09 '19 at 18:51