2

So in sql a common thing to do is a select statement with a group by and then a having count =1 or what have you.

select bID from tableA groubBy bID having count(*) = 1

Essentially I am looking to do the above but in typescript.

I also would like to do it without any additional plugins so really I would like to do this in javascript. Based on some googling I have done as well as visiting SO I have found the .reduce function to be more or less what I need (as far as I know)

The problem is I get about here and all I get are compile errors or conversion errors. Saying that it has an implicit type of any or does not have a callback function of .. etc...

What I am trying to do is pass in an array. Group them by a value. Then see how many of them their are. If the count is equal to 1 set a property equal to true for each that meet that criteria, and then exit the method.

Here is what I got from the internet.

groupByArray(xs, key) {
    return xs.reduce(function (rv, x) {
        let v = key instanceof Function ? key(x) : x[key];
        let el = rv.find((r) => r && r.key === v);
        if (el) { el.values.push(x); } else { rv.push({ key: v, values: [x] }); }
        return rv;
    }, []);
}

PS. for whatever reason people like to use very short variable names here and it confuses me. I have no idea what xs or rv is or means or what is inside of that variable so if you could help explain the code I pasted as well that might be all I need.

Thanks again.

PPS. a quick data example would be

[{bID:1,isDeleted:false},{bID:1,isDeleted:true},{bID:2,isDeleted:false},{bID:1,isDeleted:true},{bID:3,isDeleted:false},{bID:3,isDeleted:false},{bID:4,isDeleted:false}]

so what should be returned is[{bId:1,isDeleted:false}{bID:2,isDeleted:false},{bID:4,isDeleted:false}]

Below is a code example of what I could get to work.

 var test = this.user2.scc.sA.reduce((a, b) => (a.Bid== b.Bid && !a.isDeleted) ? a : b);

Edit: Quick edit and I apologize for the confusion. But the syntax I am writing in is Typescript. I know typescript is javascript but in my original question I mentioned javascript because I wanted to avoid messing with prototypes as this method is not best practice.

jtslugmaster08
  • 182
  • 1
  • 16
  • where do you get the code from? – Nina Scholz Oct 09 '18 at 20:32
  • @NinaScholz https://stackoverflow.com/questions/14446511/what-is-the-most-efficient-method-to-groupby-on-a-javascript-array-of-objects In a comment by Michael Sandino under the response that has been marked as correct – jtslugmaster08 Oct 09 '18 at 20:36

3 Answers3

3

You could simplify the function for grouping and take that result for filtering to get same grouped objects in an array and then filter this for getting only the result sets with a length of one. If necessary take the obejects out of the array.

function groupBy(array, key) {
    return array.reduce((accumulator, object) => {
        var temp = accumulator.find(array => array[0][key] === object[key]);
        if (temp) {
            temp.push(object);
        } else {
            accumulator.push([object])
        }
        return accumulator;
    }, []);
}

var array = [{ bID: 1 }, { bID: 1 }, { bID: 2 }, { bID: 1 }, { bID: 3 }, { bID: 3 }, { bID: 4 }],
    result = groupBy(array, 'bID')
        .filter(({ length }) => length === 1)
        .map(([v]) => v);
    
console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
3

Instead of grouping & filtering afterwards you could just sort by the value and remove all values that appear twice in a row:

 function unique(array, key) {
   return array
     .sort((a, b) => a[key] > b[key] ? 1 : -1)
     .filter((el, index) => 
       (index < 1 || el[key] !== array[index - 1][key]) &&
       (index >= array.length - 1 || el[key] !== array[index + 1][key])
    );
 }

That can be used as:

 unique([{bID:1},{bID:1},{bID:2},{bID:1},{bID:3},{bID:3},{bID:4}], "bID")
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • I am getting a type error whenever I use your code. It comes back with this. Cannot read property 'bID' of undefined But I know that the property is defined when I debug and look through the object. – jtslugmaster08 Oct 10 '18 at 15:04
  • @jtslumaster08 I made an edit to fix that, can you try with the newest version? – Jonas Wilms Oct 10 '18 at 15:38
  • yes it no longer errors. I edited my question but I have one more curve ball. Now if the user ever removes a row. I don't delete it I flip a isdeleted flag. So using your code I tried to do this."&&(!el.isDeleted===true)" but it still only returns 2 records. – jtslugmaster08 Oct 10 '18 at 15:59
  • 1
    @jtslugmaster08 so you want to completely ignore values that are deleted? maybe just `filter` them out before doing anything else? – Jonas Wilms Oct 10 '18 at 18:14
  • 1
    … that did it. I don't know why I couldn't think of that lol Thanks again. All I had to do when I called your method was to do a myarray.filter(o=>!o.isDeleted) and that was it. So it looked something like this. var test = unique(myArray.filter(o=>!o.isDeleted),"bID"); /*my additional code here*/ – jtslugmaster08 Oct 10 '18 at 18:58
1

you should be able to do this with one reduce function.

and then you can extend that to a reusable function like the other answers show.

function groupUnique(data, key) {
    return data.reduce((accumulator, element) => {
    const targetIdx = accumulator.findIndex(el => el[key] === element[key]);
    if (targetIdx > -1) {
        return accumulator.splice(targetIdx, 1);
    }
    return accumulator.concat(element)
}, [])}
Joey
  • 174
  • 2
  • 4