Get get unique ObjectID's from an array containing duplicates - javascript

So I have an array of ObjectID's, for example:
console.log(objectIdArray);
gives [ObjectID, ObjectID, ObjectID].
However there are duplicates here, as seen when mapping to ID strings:
var idArray = objectIdArray.map(objectId => objectId.toString());
console.log(idArray);
gives ["5afa54e5516c5b57c0d43227", "5afa54e5516c5b57c0d43227", "5afa54f0516c5b57c0d43228"] where you can see the ID ending in 27 is duplicated twice.
How can I filter this array of ObjectID's to remove the duplicates (keeping the complete ObjectID objects, not just the ID string values)?

const removeDuplicates = inputArray => {
const ids = [];
return inputArray.reduce((sum, element) => {
if(!ids.includes(element.toString()){
sum.push(element);
ids.push(element.toString());
}
return sum;
}, []);
};
This solution will remove all the objects that aren't the first object with a certain Id.
We fill an Array with the ids, then we check if the ids already are filled in the current list.
The above solution can be potentially slow if there is a lot of elements since you need to check the list of ids which is O(n) for each iteration in the inputArray which would put the algorithm at O(n^2)+O(n)
So instead, we can sort it first based on toString() then we can just verify that the current id didn't match the last id we saw.
const removeDuplicates = inputArray => {
const sortedArray = inputArray.sort((a,b) => (a.toString() > b.toString() ? 1 : (a.toString() < b.toString() ? -1 : 0)));
let lastSeen = undefined;
return sortedArray.reduce((sum, element) => {
if(lastSeen !== element.toString()){
sum.push(element);
}
lastSeen = element.toString();
return sum;
}, []);
};
Now the algorithm is O(n log n) + O(n) assuming sort uses Merge Sort

If you use ES6 you can take Sajeetharan's answer, but create a set of objects, not their ids:
let nodupes = [...new Set(objectIdArray)];

I recommend MongoDB Aggregation Pipeline to prevent the situation of having a final result with duplicate ObjectId values.
However:
// Define callback Function to receive modified Array
var receiveModifiedArray = function(objectIdArray) {
// log modified Array to console
console.log(objectIdArray);
}
// Remove duplicate ObjectId values
function removeDuplicateObjectIdValues(objectIdArray, callback) {
// Iterate through each ObjectId
objectIdArray.forEach((currentValue, index, array) => {
// evaluate Array items with index greater than 0
if(index > 0) {
// check ObjectId string values for type and value equality
if(currentValue.toString() == array[index -1].toString()) {
/**
* The splice() method changes the contents of an array
* by removing existing elements and/or adding new elements.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
*/
objectIdArray.splice(index,1);
}
// If processing last item in Array, callback
if(index == array.length +1) {
callback(objectIdArray);
}
}
});
// Return to move on to next message in call stack
return;
}
// Remove duplicate ObjectId values
removeDuplicateObjectIdValues(objectIdArray,receiveModifiedArray);

Related

get the index of two elements and manipulate the array accordingly

I build an array with the following code:
var intestColonne = [];
$('.tbi').find('tr:first th').each(function(){
intestColonne.push(($(this).children('select').val()));
});
//intestColonne=intestColonne.pop(); //if this row is parsed the array becomes undefined
Now I want to check if there are multiple entries of a specific value in the array:
if(intestColonne.filter(x => x === "importo").length>1){
//check the index of each "importo" element
//store it into variables
//remove all the other "importo" leaving only the first (lowest index)
}
I am stuck at the first step since I haven't found a specific function that may return all the indexes of the "importo" value.
indexOf will return the first index, lastIndexOf will return the last. Using indexOf I can specify where to start looking from but this will hardly satisfy my goal.
Isn't there another type of search I can use?
You can use map to get the indexes of the elements before filter.
intestColonne.map((x, i) => x === 'importo' ? i : null).filter(x => x != null);
Alternatively, use Array#reduce.
intestColonne.reduce((acc, curr, i) => {
if (curr === 'importo') acc.push(i);
return acc;
}, []);
You can use the .map() method to create an array of all the indexes of a specific value in the array. Here is an example:
var importoIndexes = intestColonne.map((value, index) => value === "importo" ? index : '').filter(String);
This will create an array importoIndexes containing all the indexes of the value "importo" in the intestColonne array.
You can then use the .slice() method to remove all the other "importo" leaving only the first (lowest index)
intestColonne = intestColonne.slice(0, importoIndexes[0]).concat(intestColonne.slice(importoIndexes[0] + 1));
Javascript array filter method's second argument supplies index of an element. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
So you can get index of first element using indexOf and then filter it:
const index = intestColonne.indexOf("importo");
const result = intestColonne.filter( (o,i) => o !== "importo" || i === index );

Get the object that has a certain number in it from an object within an object

I make an API call and it gives back the following:
[
[1529539200,15.9099,16.15,15.888,16.0773,84805.7,1360522.8],
[1529625600,16.0768,17.38,15.865,17.0727,3537945.2,58937516],
[1529712000,17.0726,17.25,15.16,15.56,3363347.2,54172164],
[1529798400,15.55,16.0488,15.3123,15.6398,2103994.8,33027598],
[1529884800,15.6024,15.749,13.3419,14.4174,3863905.2,55238030],
[1529971200,14.4174,15.1532,13.76,14.8982,2266159.8,33036208],
...
]
There are basically about 1000 objects in total, and every object has 7 objects within it, each of them containing the values shown above. Right now I have set
var objects= response.data.result[86400]
which gives the result you see above, and now, I need to search through these objects until Javascript finds the object that has the value '1529884800' in object zero, so for example with the code above this would result in this number:
object[5][0]
I wrote the following ode but it doesn't work, results in an empty array as response.
var results = [];
var toSearch = 1529539200;
for (var i=0; i<objects.length; i++) {
for (key in objects[i][0]) {
if (objects[i][key].indexOf(toSearch) != -1) {
results.push(objects[i]);
}
console.log(results)
}
}
(in the above results just shows [])
I tried doing var toSerach ='1529539200' and without quotes but neither work, what is the issue here? Would appreciate any help, thanks
If you want the index of a given number, use .flatMap() and .indexOf()
First iterate through the outer array
array.flatMap((sub, idx) =>
Then on each sub-array find the index of the given number. .indexOf() will either return the index of the number if it exists or -1 if it doesn't. In the final return it will be the index number of the sub-array and then the index of the number within the sub-array if found. Otherwise an empty array is returned which results in nothing because .flatMap() flattens arrays by one level.
sub.indexOf(number) > -1 ? [idx, sub.indexOf(number)] : [])
const data = [[1529539200,15.9099,16.15,15.888,16.0773,84805.7,1360522.8],[1529625600,16.0768,17.38,15.865,17.0727,3537945.2,58937516],[1529712000,17.0726,17.25,15.16,15.56,3363347.2,54172164],[1529798400,15.55,16.0488,15.3123,15.6398,2103994.8,33027598],[1529884800,15.6024,15.749,13.3419,14.4174,3863905.2,55238030],[1529971200,14.4174,15.1532,13.76,14.8982,2266159.8,33036208]];
let A = 1529539200;
let B = 33036208;
let C = 15.16;
const findNumber = (array, number) =>
array.flatMap((sub, idx) =>
sub.indexOf(number) > -1 ? [idx, sub.indexOf(number)] : [])
console.log(findNumber(data, A));
console.log(findNumber(data, B));
console.log(findNumber(data, C));

Sort an array of objects based on frequency but keep repeated elements

I have an array of objects,
const myArray = [{id:1, k_id:1},{id:2, k_id:2},{id:3, k_id:1},{id:4, k_id:3},{id:5, k_id:3},{id:6, k_id:2},{id:7, k_id:2},{id:8, k_id:4}];
myArray.sort((a, b) => a.k_id - b.k_id);
console.log(myArray);
I want it to be sorted based on the k_id and it occurrences(descending frequency). But, have to keep all the elements as I have other values in the objects. Other key, value pairs can be in any order. (I have simplified my issue here with only two key, value pairs but the actual array have more than 15 key, value pairs)
Output Produced:
(8) [{id:1,k_id:1},{id:3,k_id:1},{id:2,k_id:2},{id:6,k_id:2},{id:7,k_id:2},{id:4,k_id:3},{id:5,k_id:3},{id:8,k_id:4}]
Expected output, because I need them to be sorted like below as k_id:2 occured more than k_id:1:
myArray = [{id:6, k_id:2},{id:7, k_id:2},{id:2, k_id:2},{id:3, k_id:1},{id:1, k_id:1},{id:4, k_id:3},{id:5, k_id:3},{id:8, k_id:4}];
Looking for something like this?
inp.sort((a, b) =>
inp.filter(c => c.k_id === b.k_id).length -
inp.filter(c => c.k_id === a.k_id).length
);
// sorting a vs b by counting the occurency of each k_id property value
// using filter
const inp = [{id:1, k_id:1},{id:2, k_id:2},{id:3, k_id:1},{id:4, k_id:3},{id:5, k_id:3},{id:6, k_id:2},{id:7, k_id:2},{id:8, k_id:4}];
console.log(
inp.sort((a, b) => inp.filter(c => c.k_id === b.k_id).length - inp.filter(c => c.k_id === a.k_id).length)
)
try this out, I'm not sure if it would scale but, it works fine.
const myArray = [{id:1, k_id:1},{id:2, k_id:2},{id:3, k_id:1},{id:4, k_id:3},{id:5, k_id:3},{id:6, k_id:2},{id:7, k_id:2},{id:8, k_id:4}];
(()=>{
const keys = {}
const newArray = [];
/**
* Determenin every keys count
*/
for(const one of myArray){
// if the key is not yet registered in keys
// initialize 0 and add one either way
// on the key count
keys[one.k_id] = (keys[one.k_id] || 0) + 1;
}
console.log(keys)
//
function GetTheHighestFrequency () {
/**
* #return {object} highest
*
* containing a key or K_id
* and its frequency count
*/
let highest = { key:0,frequency:0 };
for(const [key,value] of Object.entries(keys)){
if(value > highest.frequency)
highest = { key,frequency:value };
}
return highest
}
//
// return new array
for(const each of Object.keys(keys)){
// request the highest frequency key K_id
const highest = GetTheHighestFrequency();
//
// Add (Push) objects in the newArray
//
for(const one of myArray){
// add an object if
// if the K_id matches the current
// highest key value
if(String(one.k_id) === highest.key)
newArray.push(one)
}
delete keys[highest.key]
}
console.log("the result is = ",newArray)
})()
I would suggest first creating a frequency lookup. Below I've used reduce with a Map, but you can use a regular object and a for loop to build the same look up. The Map has keys which are the k_id, and the value of each k_id is the number of times k_id occurs. Creating the lookup means you don't need to loop through your array each iteration of your sort. You can then use .sort() and sort by the occurrences for each key_id stored within the frequency map. As this uses .sort(), the sort is stable, so items with the same k_id will keep their relative ordering from the original array:
const myArray = [{id:1, k_id:1},{id:2, k_id:2},{id:3, k_id:1},{id:4, k_id:3},{id:5, k_id:3},{id:6, k_id:2},{id:7, k_id:2},{id:8, k_id:4}];
const freq = myArray.reduce((acc, {k_id}) => acc.set(k_id, (acc.get(k_id) || 0) + 1), new Map);
myArray.sort((a, b) => freq.get(b.k_id) - freq.get(a.k_id));
console.log(myArray);

How to edit value of an object using the reference?

I have a big object array persons
persons = [{name:'john1'}, {name:'john2'},...]
I iterated the array and found the object I am interested to edit
objectToEdit = persons .find((person)=>person.name==='john1')
Now I created an edited object in immutable way (someOtherPerson = {name:'johnxx'})
objectFinal = {...objectToEdit, someOtherPerson}
Now I want to replace this objectFinal with objectToEdit in persons array, without having to traverse the array again. But doing objectToEdit =objectFinal , will just assign objectToEdited's reference to objectToEdit , without making any change in the persons array
Is there a clean way to achieve this without traversing the array?
Edit:
In this example, the object in persons jave just one key (i.e, name). This is to make question minimal. In my project, I have more than 30 keys.
If you want to edit an object in a list,in place, use Array.prototype.some
var persons = [{
name: 'john1'
}, {
name: 'jack5'
}]
var someOtherPerson = {
name: 'johnxx'
}
persons.some(function(person) {
// if condition, edit and return true
if (person.name === 'john1') {
// use Object.keys to copy properties
Object.keys(someOtherPerson).forEach(function(key) {
person[key] = someOtherPerson[key]
})
// or use assign to merge 2 objects
Object.assign(person, someOtherPerson);
return true // stops iteration
}
})
console.log(JSON.stringify(persons))
If you want to avoid mutating the objects in the original array, you might use .findIndex instead, and reassign the item in the array at that index:
const persons = [{name:'john1'}, {name:'john2'}];
const objectToEditIndex = persons.findIndex((person) => person.name === 'john1');
const someOtherPerson = {name:"johnxx"};
persons[objectToEditIndex] = {
...persons[objectToEditIndex],
...someOtherPerson
};
console.log(persons);
Doing this:
objectFinal = {...objectToEdit, someOtherPerson}
you lose the reference to the original objectToEdit object. To edit it, you can do
`objectToEdit.value = otherValue`.
PS: Having said that, you only lose reference to the first level object. If that object, contains another object or array, you have reference to it:
objectFinal.innerObject.value = otherValue;
{name:'john1'} should be replace with {name:"johnxx"} in persons
Reassign persons to the persons-array mapped with the new value:
let persons = [{name:'john1'}, {name:'john2'}];
persons = persons.map( v => v.name === "john1" ? {name: "johnxx"} : v );
console.log(persons);
Or, if you're worried about performance, use Array.splice in combination with Array.findIndex
The findIndex method executes the callback function once for every
array index 0..length-1 (inclusive) in the array until it finds one
where callback returns a truthy value (a value that coerces to true).
If such an element is found, findIndex immediately returns the index
for that iteration.
let persons = [{name:'john1'}, {name:'john2'}];
persons.splice(persons.findIndex( v => v.name==="john1" ), 1, {name:'johnxx'});
console.log(persons);
Or use a utility method (snippet for a large array (1,000,000 elements) and with performance timer)
// replacer method
const replace = (obj, [key, value], replacement) => {
obj.splice(persons.findIndex( v => v[key] === value ), 1, replacement);
return obj;
}
// create a large array (this may take some extra time)
let persons = Array.from({length: 1000000}).map(v =>
({name: Math.floor(100000 + Math.random() * 1000000000).toString(16)}) );
// pick an entry
const someEntry = Math.floor(persons.length * Math.random());
const entry = Object.entries(persons[someEntry])[0];
console.log("before:", persons[someEntry]);
const t = performance.now();
// replacement call here
console.log("after:", replace(persons, entry, {name: "johnxx"})[someEntry]);
console.log("replacement took:", `${((performance.now() - t)/1000).toFixed(3)} sec`);

Javascript: Find douplicated values from array with keys

Title is pretty much self explanatory...
I want to be able to find duplicated values from JavaScript array.
The array keys can be duplicated so I need to validate only the array values.
Here is an example :
var arr=[
Ibanez: 'JoeSatriani',
Ibanez: 'SteveVai',
Fender: 'YngwieMalmsteen',
Fender: 'EricJohnson',
Gibson: 'EricJohnson',
Takamine: 'SteveVai'
];
In that example:
the key is the guitar brand
the value is the guitar player name.
So:
If there is duplicated keys (like: Ibanez or Fender) as on that current example that is OK :-)
But
If there is duplicated values (like: EricJohnson or SteveVai) I'm expecting to get (return) that error:
EricJohnson,SteveVai
You can't have associative arrays in Javascript. You can create an array of objects, like:
var arr=[
{Ibanez: 'JoeSatriani'},
{Ibanez: 'SteveVai'},
{Fender: 'YngwieMalmsteen'},
{Fender: 'EricJohnson'},
{Gibson: 'EricJohnson'},
{Takamine: 'SteveVai'}
];
Then you'll need a for...in loop to go over every object in the array, create a new array of values and check that for duplicates, which is also not very straightforward - basically you'll want to sort the array and make sure no value is the same as the one after it.
var arrayOfValues = [];
arr.forEach(function(obj){
for(var prop in obj)
arrayOfValues.push(obj[prop]);
});
arrayOfValues.sort(); // by default it will sort them alphabetically
arrayOfValues.forEach(function(element,index,array){
if(array[index+1] && element==array[index+1])
alert("Duplicate value found!");
});
First of all, object keys can not be repeated.
This means that:
({
"Fender": "Jim",
"Fender": "Bob"
})["Fender"]
Would simply return: "Bob".
However, I did make a code that could allow you to find duplicates in values, but as I said, the key will have to be unique:
var arr = {
Ibanez: 'EricJohnson',
Fender: 'YngwieMalmsteen',
Gibson: 'EricJohnson',
Takamine: 'SteveVai',
"Takamine2": 'SteveVai'
};
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
var track = [];
var exists = [];
for (var val in arr) {
if (contains(track, arr[val])) {
exists.push(arr[val]);
} else {
track.push(arr[val])
}
}
alert(exists)
You can see it working here: http://jsfiddle.net/dr09sga6/2/
As others have commented, the example array you provided isn't a valid JavaScript array. You could, however, keep a list for each guitar type:
var mapping = {
Ibanez: ['JoeSatriani','SteveVai'],
Fender: ['YngwieMalmsteen','EricJohnson']
Gibson: ['EricJohnson'],
Takamine: ['SteveVai']
];
Or a list of each guitar/musician pair:
var pairs = [
['Ibanez','JoeSatriani'],
['Ibanez','SteveVai'],
['Fender','YngwieMalmsteen'],
['Fender','EricJohnson'],
['Gibson','EricJohnson'],
['Takamine','SteveVai']
];
Your solution is going to depend on which pattern you go with. However, in the second case it can be done in one chained functional call:
pairs.map(function(e) {return e[1]}) // Discard the brand names
.sort() // Sort by artist
.reduce(function(p,c,i,a){
if (i>0 && a[i]==a[i-1] && !p.some(function(v) {return v == c;})) p.push(c);
return p;
},[]); //Return the artist names that are duplicated
http://jsfiddle.net/mkurqmqd/1/
To break that reduce call down a bit, here's the callback again:
function(p,c,i,a){
if (i>0
&& a[i]==a[i-1]
&& !p.some(function(v) {
return v == c;
}))
p.push(c);
return p;
}
reduce is going to call our callback for each element in the array, and it's going to pass the returned value for each call into the next call as the first parameter (p). It's useful for accumulating a list as you move across an array.
Because we're looking back at the previous item, we need to make sure we don't go out of bounds on item 0.
Then we're checking to see if this item matches the previous one in the (sorted) list.
Then we're checking (with Array.prototype.some()) whether the value we've found is ALREADY in our list of duplicates...to avoid having duplicate duplicates!
If all of those checks pass, we add the name to our list of duplicate values.

Categories

Resources