user9945420
user9945420

Reputation:

get the length of an array with unique values

so I have an array containing some objects. I want to compare these objects with each other. If the fromId AND the toId properties are the same, it is an duplicate.

Example:

const data = [{
  fromId: 1,
  toId: 2,
  finished: true
}, {
  fromId: 1,
  toId: 2,
  finished: false
}, {
  fromId: 5,
  toId: 9,
  finished: false
}, {
  fromId: 1,
  toId: 5,
  finished: true
}, {
  fromId: 2,
  toId: 1,
  finished: false
}];

$(document).ready(() => {
  const duplicates = data.filter(x =>
    data
    .filter(y => y.fromId == x.fromId && y.toId == x.toId)
    .length > 1
  );

  console.log(duplicates);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

And I want to get the length of this data array but only with unique values. I tried to remove all duplicate values and found a solution here

Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array

const data = [1, 2, 3, 4, 5, 2, 2, 6, 7, 8, 2, 9, 2, 2, 2, 2, 2];

$(document).ready(() => {
  const uniqueValues = data.filter((connection, index) => data.indexOf(connection) == index);

  console.log(uniqueValues);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

but on this solution I just use indexOf for the whole object. How can I compare the fromId and toId and remove the duplicate on same matches?

From the original array I would remove one

{
      fromId: 1,
      toId: 2,
      finished: false
    }

because the fromId and toId exist already at another object.

Upvotes: 3

Views: 1428

Answers (6)

mplungjan
mplungjan

Reputation: 178160

Have a go with a simple forEach - this is plain JS and not ES5/6 or jQuery

const data = [{fromId: 1,toId: 2,finished: true},{fromId: 1,toId: 2,finished: false}, {fromId: 5,toId: 9,finished: false}, {fromId: 1,toId: 5,finished: true}, {fromId: 2, toId: 1, finished: false }];
var combos = [];
var  dupes = [];
var unique = [];

data.forEach(function(item){
  var combo = ""+item.fromId+"."+item.toId;
  if (combos.indexOf(combo) == -1) {
    combos.push(combo);
    unique.push(item);
  }
  else {
    dupes.push(item);
  }
});
console.log("dupes",dupes);
console.log("unique",unique,unique.length);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Upvotes: 0

stas.yaranov
stas.yaranov

Reputation: 1787

Yeah, many options available... Will give you another one with lodash/fp:

Assuming you have:

import {flow, map, uniq} from 'lodash/fp'

Do this:

const count = flow(
    map(o => o.fromId + '.' + o.toId),
    uniq,
  )(data).length

console.log('Count: ', count)

Upvotes: 0

Capricorn
Capricorn

Reputation: 2089

While CertainPerformance's answer is probably better for your scenario, the indexOf alternative that does not compare item identities would be findIndex. However, this is ES6+ only, but most modern browsers implement it (sorry, no IE11).

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

Upvotes: 0

Damian Peralta
Damian Peralta

Reputation: 1876

You can create a new array, and push the lements only once, checking if they already exists or not using inArray function from jQuery:

const data = [{
  fromId: 1,
  toId: 2,
  finished: true
}, {
  fromId: 1,
  toId: 2,
  finished: false
}, {
  fromId: 5,
  toId: 9,
  finished: false
}, {
  fromId: 1,
  toId: 5,
  finished: true
}, {
  fromId: 2,
  toId: 1,
  finished: false
}];

$(document).ready(() => {
   
  var filtered = [];
  $.each(data, function(i, item){
      if($.inArray(item, filtered) === -1){
        filtered.push(item);
      }
      
      
  });
  console.log(filtered.length);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

jQuery inArray()

Upvotes: 0

Ankit Agarwal
Ankit Agarwal

Reputation: 30739

You can use Array.reduce() to filter the array with unique objects and then get the length value together with the unique array:

const data = [{
  fromId: 1,
  toId: 2,
  finished: true
}, {
  fromId: 1,
  toId: 2,
  finished: false
}, {
  fromId: 5,
  toId: 9,
  finished: false
}, {
  fromId: 1,
  toId: 5,
  finished: true
}, {
  fromId: 2,
  toId: 1,
  finished: false
}];

$(document).ready(() => {
  var uniqueArray = data.reduce((acc, obj)=>{
    var existObj = acc.find(item => item.fromId === obj.fromId && item.toId === obj.toId);
    if(existObj){
      return acc;
    }
    acc.push(obj);
    return acc;
  },[]);
console.log('unique array ', uniqueArray);
console.log('length of unique object ', uniqueArray.length);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370929

Rather than filter nested in another filter (bad runtime complexity), since you're just looking for the length of the resulting unique array, you might instead combine each fromId and toId into a string, put that string into a Set (which only retains unique values), and then check that Set's size:

const data=[{fromId:1,toId:2,finished:!0},{fromId:1,toId:2,finished:!1},{fromId:5,toId:9,finished:!1},{fromId:1,toId:5,finished:!0},{fromId:2,toId:1,finished:!1}];

const idSet = new Set(data.map(({ fromId, toId }) => fromId + '_' + toId));
console.log(idSet.size);

Upvotes: 1

Related Questions