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.

2
  • where do you get the code from? Commented Oct 9, 2018 at 20:32
  • @NinaScholz stackoverflow.com/questions/14446511/… In a comment by Michael Sandino under the response that has been marked as correct Commented Oct 9, 2018 at 20:36

3 Answers 3

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")
Sign up to request clarification or add additional context in comments.

5 Comments

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.
@jtslumaster08 I made an edit to fix that, can you try with the newest version?
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 so you want to completely ignore values that are deleted? maybe just filter them out before doing anything else?
… 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*/
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);

Comments

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)
}, [])}

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.