0

I have a list of objects that contain peoples names and their service times. These names can occur more than once throughout this list, and the service times can be different for each occurrence. What I'm trying to do is create a HashSet of names with an index attached to it so that I can reference it back to the larger list without having to create a nested for loop. For instance if I have the list:

[{Name: 'john doe', ServiceTime: '20min'}, {Name: 'john doe', ServiceTime: '25min'},
 {Name: 'john doe', ServiceTime: '31min'}, {Name: 'billy bob', ServiceTime: '1min'},
 {Name: 'billy bob', ServiceTime: '12min'}, {Name: 'john doe', ServiceTime: '19min'}]

And i create my HashSet to look like this:

{'john doe': 0, 'billy bob': 1}

Here is how I'm doing it:

let count = 0;
 list.map(obj => arr[obj.Name] = arr[obj.Name] ? arr[obj.Name] : count++)

The value of the key is incremented each time a NEW person is found, if that makes sense. After this is complete I iterate through the new object of names that I've created, to create an array of objects, which will hold the persons name and an array to hold their service times like so:

[{Name: 'john doe', ServiceTimes:[]}, {Name: 'billy bob', ServiceTimes:[]}]

With this information I can go back and iterate through my initial list updating the index to search for by the key value pair of my HashSet like this:

list.map(obj => { 
            let index = arr[obj.Name];

            if(temp[index] !== undefined){
                temp[index].ServiceTimes.push(obj)
            }
        })

The issue that I'm facing is that after creating my HashSet, I notice that I'm getting an off by 1 error with my indices. For example, the list that i provided above gives me the HashSet:

{'john doe': 1, 'billy bob': 2}

and with other lists i get the HashSet

{'john doe': 0, 'billy bob': 1}

I obviously want my HashSet to look like the last one but can't seem to understand why my count is being added twice in some scenarios and not in others. If anyone could point me in the right direction or understands what I might be doing wrong that's causing this issue, I would greatly appreciate it. Thanks!

P.S. Sorry for the long question.

Michael
  • 1,454
  • 3
  • 19
  • 45
  • Do not use `map` for looping when you don't want to create a new array. Use a normal loop for looping, like `for (const obj of list)` – Bergi Mar 19 '20 at 19:43
  • Can you please share your entire code, as one runnable snippet? – Bergi Mar 19 '20 at 19:43
  • @Bergi Thank you, I'm trying to be better at using the correct methods in specific situations like this. – Michael Mar 19 '20 at 19:51
  • Then also use a `new Map` instead of an object for the "hash map" :-) – Bergi Mar 19 '20 at 19:53
  • @Bergi I haven't used ```Map``` yet but I have been looking at it. This is the only way I knew how at the time of writing it. – Michael Mar 19 '20 at 19:56
  • 1
    @Michael update your question with correct JSON formatted array. names and times need quotes around them. – MonteCristo Mar 19 '20 at 20:08
  • @MonteCristo Ok I have edited my question with the correct JSON format. – Michael Mar 19 '20 at 20:24

2 Answers2

2

You can just use reduce() to iterate over array. And when you run the reducer you can do anything within it. As shown below.

let data = [
 {Name: 'john doe', ServiceTime: '20min'},
 {Name: 'john doe', ServiceTime: '25min'},
 {Name: 'john doe', ServiceTime: '31min'},
 {Name: 'billy bob', ServiceTime: '1min'},
 {Name: 'billy bob', ServiceTime: '12min'},
 {Name: 'john doe', ServiceTime: '19min'}];

let mappedData = data.reduce((accumulator, currentItem, index) => {
      const {Name, ServiceTime} = currentItem;
      if(!accumulator.hasOwnProperty(Name)) {
        accumulator[Name] = {
          firstMatchedAtindex: index,
          count: 0,
          serviceTimes: []
        };
      }

      accumulator[Name].count += 1 ;
      accumulator[Name].serviceTimes.push({index, ServiceTime});
      return accumulator;
      
}, {})

console.log(mappedData);

Simpler script...

let data = [
 {Name: 'john doe', ServiceTime: '20min'},
 {Name: 'john doe', ServiceTime: '25min'},
 {Name: 'john doe', ServiceTime: '31min'},
 {Name: 'billy bob', ServiceTime: '1min'},
 {Name: 'billy bob', ServiceTime: '12min'},
 {Name: 'john doe', ServiceTime: '19min'}];

let mappedData = data.reduce((accumulator, currentItem, index) => {
      const {Name, ServiceTime} = currentItem;
      if(!accumulator.hasOwnProperty(Name)) {
        accumulator[Name] = index;
      }

      return accumulator;
      
}, {})

console.log(mappedData);

Also note array map callback function give you access to index. e.g.

const list = [
 {Name: 'john doe', ServiceTime: '20min'},
 {Name: 'john doe', ServiceTime: '25min'},
 {Name: 'john doe', ServiceTime: '31min'},
 {Name: 'billy bob', ServiceTime: '1min'},
 {Name: 'billy bob', ServiceTime: '12min'},
 {Name: 'john doe', ServiceTime: '19min'}];

 const transformed = list.map((obj, index) => { 
   return {
     ...obj,
     index
   }
 });
console.log(transformed);

// Same with forEach alternative to for loop
list.forEach((obj, index) => { 
    console.log(obj, index);
 });
 
MonteCristo
  • 1,471
  • 2
  • 20
  • 41
  • I don't want the number of occurrences, i want the value to reflect each time a NEW person is found. For "john doe" the index should be "0" because he is the first name in the set. "billy bob" would be "1" because he is the second name in the set... and so forth – Michael Mar 19 '20 at 19:50
  • I've added a property called `firstMatchedAtIndex` that will hold the index of the name's first match. Bottom line is you can use reduce and do anything with it. – MonteCristo Mar 19 '20 at 19:57
  • this works with the data set given but if you try this instead: ```[{Name: 'john doe', ServiceTime: '20min'}, {Name: 'billy bob', ServiceTime: '12min'}, {Name: 'billy bob', ServiceTime: '21min'}]``` You will notice that the data set that you've used gives you ```john``` with a value of ```1``` and ```billy``` with a value of ```2```. And with the data set that I just gave you get ```john``` with a value of ```0``` and ```billy``` with a value of ```1```. This has been my dilemma with the way I have it currently as well. – Michael Mar 19 '20 at 20:12
  • 1
    I've updated my answer. issue with my code at when `index = 0` and property has value 0 when I just do `!accumulator[name]` is false because `!accumulator[name] = 0 === false` . I've corrected it now. – MonteCristo Mar 19 '20 at 20:33
1
let count = 0;
list.map(obj => arr[obj.Name] = arr[obj.Name] ? arr[obj.Name] : count++)

You never reset the count variable. You should reset the count variable upon finding a new person name.

let data = [
  {Name: 'john doe', ServiceTime: '20min'}, 
  {Name: 'john doe', ServiceTime: '25min'},
 {Name: 'john doe', ServiceTime: '31min'}, 
 {Name: 'billy bob', ServiceTime: '1min'},
 {Name: 'billy bob', ServiceTime: '12min'}, 
 {Name: 'john doe', ServiceTime: '19min'}
];

let final = {};
let found = 0;
data.forEach((obj, index) => {
  if (!final.hasOwnProperty(obj.Name)) {
    final[obj.Name] = found;
    found += 1;

    return;
  }
})

console.log(final);
Bruno Francisco
  • 3,841
  • 4
  • 31
  • 61
  • 2
    OP said they want the "hash set" to hold an *index*, not a count of persons with that name. – Bergi Mar 19 '20 at 19:44
  • @Bergi is right, i don't want the number of times a name appears. I want to know that ```john doe``` was the first name so his value should be ```0```. ```billy bob``` was the second name found so his value should be ```1``` and so on... – Michael Mar 19 '20 at 19:54
  • @Michael edit made to the question to address the issue – Bruno Francisco Mar 19 '20 at 19:57
  • ```john doe``` has a value of ```0``` but ```billy bob``` has a value of ```2``` after doing this. billy should have a value of 1. – Michael Mar 19 '20 at 20:00
  • @Michael I misunderstood it for the second time. Updated the question – Bruno Francisco Mar 19 '20 at 20:03
  • After doing some testing, it looks like the solution you gave me works. I have upvoted it for now, until I do a little more testing, I will accept this as the answer. Thanks! – Michael Mar 19 '20 at 20:21