I am writing a program that needs to compare different birthday dates in an array, and the array is like this:
birthdays = [1, [11,2], 2, [2,4], 3, [11,2], 4, [3,5], 5, [6,7], 6, [3,5], 7, [8,9]..]
The even index stores the number of student, and the odd positions store their birthday date in the form of an array[month, day]. I need to return an array of number of students who have unique birthday dates(their birthday dates only appeared once in the array).
Here is my effort so far:
function find(birthdays) {
let array2 = birthdays.slice();
let unique_arr = [];
let element_comp;
for (let i = 0; i < birthdays.length; i++) {
birthdays.splice(i,1)//birthdays
array2.splice(i+1,1)//numbers
}
for(let i = 0; i < birthdays.length; i++){
element_comp = birthdays[i];
for(let j = 0; j < birthdays.length; j++){
if(i !== j &&element_comp.toString == birthdays[j].toString){
break;
}
if(j === birthdays.length - 1){
unique_arr.push(array2[i])
}
}
}
return unique_arr;
}
My idea is to break the array into to two sub-arrays, one stores the numbers and the other one stores the birthday dates. Then I compared the element of the birthday date array one by one to see if they are unique. If I find a same date, I break out of the inner and start to compare the next one, otherwise if the inner index reaches the end of the array I stores the element of the number array of the same index into another array that will be returned
Expected Result:[2, 5, 7...]
But it didn't work as expected because it would break out of the array each time and never store the element. Please explain to me where I did wrong, thank you
You can do it with dictiory. without having nested loop
loop on element and see if month and date combination month_date exist in dict append the students to it else create a new key with month_date and append to dict with value of array of current student
after this loop complete you can loop over dict key values and and see if any key having single value then take those values.
function find(birthdays) {
let unique_dates = {}
for (let i = 0; i < birthdays.length; i+=2) {
let key = `${birthdays[i+1][0]}_${birthdays[i+1][1]}`
if (!(key in unique_dates)) {
unique_dates[key] = []
}
unique_dates[key].push(birthdays[i])
}
let unique_arr = [];
for (let unique_date in unique_dates){
if (unique_dates[unique_date].length == 1) {
unique_arr.push(unique_dates[unique_date][0])
}
}
return unique_arr;
}
Related
Write a function getDuplicates that returns an array of all the elements that appear more than once in the initial items array (keeping the order). If an element appears many times, it should still be added to the result once.
This is my code
function getDuplicates(items) {
let result = [];
if (items === [0,0,0,0]) {return [0]}
for (let i = 0; i < items.length; i++) {
for (let j = i + 1; j < items.length; j++) {
if (items[i] === items[j]) {
result.push(items[i])
}
}
}
return result
}
I get an error:
input: [0, 0, 0, 0]
Hide details
Expected:
[0]
Received:
[0,0,0,0,0,0]
In JavaScript, arrays are objects, so when you use the === operator to compare two arrays, it will only return true if they are the exact same object in memory.
Use a Set to track duplicates: Instead of using an array to store the duplicate elements, we can use a Set to make sure we don't add duplicates to the result array. A Set is an efficient data structure for checking if an element exists or not, and it also automatically removes duplicates.
Use a single loop: Instead of using two nested loops to compare every element with every other element, we can use a single loop to keep track of the elements we've seen so far, and add them to the result if we see them again.
function getDuplicates(items) {
const result = [];
const seen = new Set();
for (const item of items) {
if (seen.has(item) && !result.includes(item)) {
result.push(item);
} else {
seen.add(item);
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
a modified version of yours
function getDuplicates(items) {
let result = [];
let added = {};
for (let i = 0; i < items.length; i++) {
if (!added[items[i]] && items.indexOf(items[i], i + 1) !== -1) {
result.push(items[i]);
added[items[i]] = true;
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
or in short doing the same
const getDuplicates = items => items.filter((item, index) => items.indexOf(item) !== index && items.lastIndexOf(item) === index);
console.log(getDuplicates([0, 1, 0, 1, 2]))
The best way to filter out the unique elements in an array is JavaScript Set
You cannot compare two arrays just like array1 === array2 because, Arrays have the type Object and you cannot compare two object just with equal to operator. Objects are not compared based on their values but based on the references of the variables. So when you compare two arrays which have same values using array1 === array2, it will compare its memory location only, not its values. So it will be only false.
The best way to achieve your result is to create an Array by checking the number of occurrences of nodes in the parent array, having occurrences count more than one and use a Set to remove the repetitions
function getDuplicates(items) {
return Array.from(new Set(items.filter(node => items.filter(x => node === x).length > 1)))
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
You can try it:
Check if the current number is duplicated by using filter to check the length of an array.
Check if the result array contains duplicates.
function getDuplicates(items) {
let result = [];
for (let i = 0; i < items.length; i++) {
if ((items.filter(item => item == items[i])).length > 1 && !result.includes(items[i])) {
result.push(items[i]);
}
}
return result;
}
console.log(getDuplicates([0, 0, 0, 0]));
So. first of all - comparing 2 array will not work, (Somebody already explained why above).
Your code doesn't work because of if statement. You're checking if an array doesn't have any value except 0.
Try summing all numbers in the array and check if it's 0.
if(arr.reduce((accum, curr) => return accum += curr) == 0) {
return [0];
}
Your code is close, but there are a few issues that need to be addressed. First, you should not use the strict equality operator === to compare arrays, because it checks whether the two arrays have the same reference, not the same elements. Instead, you can use the JSON.stringify() method to compare the string representations of the arrays.
Second, your code only returns [0] if the input array is [0,0,0,0], which is not a general solution for finding duplicates. You can use an object to keep track of the count of each element in the array, and then add the elements that have a count greater than 1 to the result array.
Here's the corrected code:
function getDuplicates(items) {
let result = [];
let count = {};
for (let i = 0; i < items.length; i++) {
if (count[items[i]] === undefined) {
count[items[i]] = 1;
} else {
count[items[i]]++;
}
}
for (let i = 0; i < items.length; i++) {
if (count[items[i]] > 1 && result.indexOf(items[i]) === -1) {
result.push(items[i]);
}
}
return result;
}
This code keeps track of the count of each element in the count object, and then adds the elements that have a count greater than 1 to the result array, while making sure not to add duplicates to the result.
The question in the title is the one that I've seen on SO before many times, and I like to share my code snipped to do that. If anyone has any improvements, I like to see them.
I used a different(?) approach.
Basically, counting from 0 to choices product, and converting to a "number" with each "digit" with it's own "base"...
var arr=[
[1,2,3,4],
[5,6,7],
[8,9,10],
[11,12],
[13,14,15]
];
var lengths=arr.map(subArr=>subArr.length);
var lengthsProd=lengths.reduce((acc,cv)=>acc*cv,1);
var masterIndexes=[];
for(var i=0; i<lengthsProd; i++) {
var indexes=[];
var index=i;
for(var j=lengths.length-1; j>=0; j--) {
var jlength=lengths[j];
indexes.unshift(index%jlength);
index=Math.floor(index/jlength);
}
masterIndexes.push(indexes);
};
var combinations=masterIndexes.map(index=>
index.map((i, idx)=>arr[idx][i])
);
console.log("masterIndexes:");
console.log(masterIndexes.join(", "));
//masterIndexes.forEach(mi=>console.log(mi.join(", ")));
console.log("-".repeat(50));
console.log("combinations:");
console.log(combinations.join(", "));
//combinations.forEach(c=>console.log(c.join(", ")));
.as-console-wrapper { max-height: 100% !important; top: 0; }
let arraySet = [
[1,2,3,4],
[5,6,7],
[8,9,10],
[11,12],
[13,14,15]
]
// We need to know how many unique combinations can be created.
// In this example it's 216.
let outputArrayLength = 1;
arraySet.forEach(array => {
outputArrayLength = outputArrayLength * array.length
});
// Let's create the output array that contains empty arrays for now.
// The number of items should be equal to outputArrayLength;
let outputArray = Array.from(Array(outputArrayLength), () => []);
// To fill the empty arrays properly, we need to know 3 things.
// 1. How many iterations are needed per item?
// We need to calculate the number of unique combinations of the arrays before the current one.
// 2. How many times should that item be pushed in each iteration?
// We need to calculate the number of unique combinations of the arrays after the current one.
// For example, when we reached the 3rd array, which is [8,9,10] in the loop,
// each array item will be pushed 6 (2 * 3) times in each iteration of 12 (4 * 3).
// 3. Does the current item belong to which array in outputArray?
// By incrementing lastIndex after each item push, we will know which array is our new target.
let previousCombinations = 1;
let remaningCombinations = outputArrayLength;
let lastIndex = 0;
arraySet.forEach(array => {
// Calculate 2
remaningCombinations = remaningCombinations / array.length;
// Push the item to the target array.
for (let i = 0; i < previousCombinations; i++) {
array.forEach(num => {
for (let i = 0; i < remaningCombinations; i++) {
outputArray[lastIndex].push(num);
if (lastIndex < outputArrayLength -1) {
lastIndex++;
} else {
lastIndex = 0;
}
}
})
}
// Calculate 1 before the next loop.
previousCombinations = previousCombinations * array.length;
})
console.log(outputArray)
There is an array that contains key value pairs which the key will contain a manipulative id. The problems is that I intend to achieve a concept of checking the key values which is if the key is exist in the array, then the value will check; or else it will create a new key value pairs for the new and unique id. I looked for the Object.keys() function and i find out it will only return array of keys. I tend to check the keys one by one in a for loop tho.
sorry if I did not explain my question well
code:
var marks = new Array();
function totalScore(critId,score){
var compare = Object.keys(marks);
var result = 0;
for(var i = 0; i < marks.length; i++){ //Looping in marks array
for(var j = 0; j < compare.length; j++){ //Looping in compare array
if(compare[j] == marks[i]){ //Comparing the key of the arrays
marks[i] = score; //If matched update the value of the current key
result ++;
}
}
if(result == 0){ //If there are no result of the comparison
marks.critId = score; //Add new key value pair to the array
}
}
}
If I understand your problem correctly, you want to add a new key/value pairs if it doesn't exist.
It's better to use an object or an ES6 Map for this case.
var marks = {};
function totalScore(critId, score) {
if(!(critId in marks)) marks[critId] = score;
}
totalScore(1, 5);
totalScore(2, 8);
totalScore(1, 4);
console.log('marks', marks);
I have a month array in javascript, for example:
2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01,
2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01,2013/04/01,
2012/09/01,2012/10/01,2012/11/01,2012/12/01
What I wanna separate the Array is that:
if (monthArray[i] > monthArray[i + 1])
// slice the Array.
So, for the above example, I should get 3 new Arrays. They are:
Array1: 2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01
Array2: 2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01,2013/04/01
Array3:2012/09/01,2012/10/01,2012/11/01,2012/12/01
I know it is easy to do it if we know specific length, my question is, how to do it if we dynamically get a month Array(it may be divided into n groups). How to do that? Thanks!
I don't know of any better way than to iterate over the array to build your slices:
var arr = ['2012/09/01','2012/10/01','2012/11/01','2012/12/01','2013/01/01','2013/02/01','2013/03/01','2012/09/01','2012/10/01','2012/11/01','2012/12/01','2013/01/01','2013/02/01','2013/03/01','2013/04/01','2012/09/01','2012/10/01','2012/11/01','2012/12/01'];
var slices = [];
var start = 0;
for (var i=0; i<arr.length; i++) {
if (check(arr, i)) {
slices.push(arr.slice(start, i+1));
start = i+1;
}
}
function check(array, index) {
if (index+1 === array.length) return true;
return Date.parse(array[index]) > Date.parse(array[index+1]);
}
This solution has the advantage that it doesn't build the slices one element at a time, instead it builds them one slice at a time.
Assuming you want an array-of-arrays as a result, you can do this with .reduce:
var partitions = dateList.reduce(function(rv, month) {
var partition = rv[rv.length - 1], prevMonth = partition[partition.length - 1];
if (!prevMonth || prevMonth < month)
partition.push(month);
else
rv.push([month]);
return rv;
}, [ [] ]);
Starting from a list of partitions with one (empty) partition, this just checks the last month in the last partition to see if it's smaller than the current month under examination. If so (or if we're on the very first one), we add the month onto the partition. If not, then a new partition is started, containing just the current month.
So assuming you want to end up with an array of arrays, then just do it with a for loop...
var result = []; //this will contain multiple arrays once finished
var currentArray = [];
for (var i = 0; i < monthArray.length; i++) {
currentArray.push(monthArray[i]);
if (i < monthArray.length - 1 && monthArray[i] > monthArray[i + 1]) {
result.push(currentArray);
currentArray = [];
}
}
result.push(currentArray);
//result[0] is Array1
//result[1] is Array2
//result[2] is Array3
Here is a working example
In a JavaScript array how can I get the index of duplicate strings?
Example:
MyArray = ["abc","def","abc"]; //----> return 0,2("abc");
Another example:
My Array = ["abc","def","abc","xyz","def","abc"]
//----> return 0,2,5("abc") and 1,4("def");
I have no idea how to do this.
Thanks in advance for your help!
Update 01/2022: It's not 2013 anymore, and many things have changed. I neither recommend modifying the prototype, nor is the approach in this answer the "best" as it requires several iterations over the array.
Here's an updated version of the original answer, retaining its spirit, as well as the original answer below.
function getDuplicates<T>(input: T[]): Map<T, number[]> {
return input.reduce((output, element, idx) => {
const recordedDuplicates = output.get(element);
if (recordedDuplicates) {
output.set(element, [...recordedDuplicates, idx]);
} else if (input.lastIndexOf(element) !== idx) {
output.set(element, [idx]);
}
return output;
}, new Map<T, number[]>());
}
Yet another approach:
Array.prototype.getDuplicates = function () {
var duplicates = {};
for (var i = 0; i < this.length; i++) {
if(duplicates.hasOwnProperty(this[i])) {
duplicates[this[i]].push(i);
} else if (this.lastIndexOf(this[i]) !== i) {
duplicates[this[i]] = [i];
}
}
return duplicates;
};
It returns an object where the keys are the duplicate entries and the values are an array with their indices, i.e.
["abc","def","abc"].getDuplicates() -> { "abc": [0, 2] }
Another less sophisticated approach:
Iterate over the whole array and keep track of the index of each element. For this we need a string -> positions map. An object is the usual data type to use for this. The keys are the elements of the array and the values are arrays of indexes/positions of each element in the array.
var map = {};
for (var i = 0; i < arr.length; i++) {
var element = arr[i]; // arr[i] is the element in the array at position i
// if we haven't seen the element yet,
// we have to create a new entry in the map
if (!map[element]) {
map[element] = [i];
}
else {
// otherwise append to the existing array
map[element].push(i);
}
// the whole if - else statement can be shortend to
// (map[element] || (map[element] = [])).push(i)
}
Now you can iterate over the map and remove all entries where the array value has a length of one. Those are elements that appear only once in an array:
for (var element in map) {
if (map[element].length === 1) {
delete map[element];
}
}
Now map contains a string -> positions mapping of all duplicate elements of the array. For example, if you array is ["abc","def","abc","xyz","def","abc"], then map is an object of the form
var map = {
'abc': [0,2,5],
'def': [1,4]
};
and you can process it further in any way you like.
Further reading:
Eloquent JavaScript - Data structures: Objects and Arrays
MDN - Working with objects
MDN - Predefined core objects, Array object
This covers finding the indices efficiently:
var inputArray = [1, 2, 3, 4, 5, 6, 6, 7, 8, 9];
var encounteredIndices = {};
for(var i = 0; i < inputArray.length; i++)
if (encounteredIndices[inputArray[i]])
console.log(i); // Or add to some array if you wish
else
encounteredIndices[inputArray[i]] = 1;