Just as title reads, I need to check whether the number of unique entries within array exceeds n.
Array.prototype.some() seems to fit perfectly here, as it will stop cycling through the array right at the moment, positive answer is found, so, please, do not suggest the methods that filter out non-unique records and measure the length of resulting dataset as performance matters here.
So far, I use the following code, to check if there's more than n=2 unique numbers:
const res = [1,1,2,1,1,3,1,1,4,1].some((e,_,s,n=2) => s.indexOf(e) != s.lastIndexOf(e) ? false : n-- ? false : true);
console.log(res);
.as-console-wrapper { min-height: 100%}
And it returns false while there's, obviously 3 unique numbers (2,3,4).
Your help to figure out what's my (stupid) mistake here is much appreciated.
p.s. I'm looking for a pure JS solution
You can use a Map() with array values as map keys and count as values. Then iterate over map values to find the count of unique numbers. If count exceeds the limit return true, if not return false.
Time complexity is O(n). It can't get better than O(n) because every number in the array must be visited to find the count of unique numbers.
var data = [1, 1, 2, 1, 1, 3, 1, 1, 4, 1];
function exceedsUniqueLimit(limit) {
var map = new Map();
for (let value of data) {
const count = map.get(value);
if (count) {
map.set(value, count + 1);
} else {
map.set(value, 1);
}
}
var uniqueNumbers = 0;
for (let count of map.values()) {
if (count === 1) {
uniqueNumbers++;
}
if (uniqueNumbers > limit) {
return true;
}
}
return false;
}
console.log(exceedsUniqueLimit(2));
To know if a value is unique or duplicate, the whole array needs to be scanned at least once (Well, on a very large array there could be a test to see how many elements there is left to scan, but the overhead for this kind of test will make it slower)
This version uses two Set
function uniqueLimit(data,limit) {
let
dup = new Set(),
unique = new Set(),
value = null;
for (let i = 0, len = data.length; i < len; ++i) {
value = data[i];
if ( dup.has(value) ) continue;
if ( unique.has(value) ) {
dup.add(value);
unique.delete(value);
continue;
}
unique.add(value);
}
return unique.size > limit;
}
I also tried this version, using arrays:
function uniqueLimit(data, limit) {
let unique=[], dup = [];
for (let idx = 0, len = data.length; idx < len; ++idx) {
const value = data[idx];
if ( dup.indexOf(value) >= 0 ) continue;
const pos = unique.indexOf(value); // get position of value
if ( pos >= 0 ) {
unique.splice(pos,1); // remove value
dup.push(value);
continue;
}
unique.push(value);
}
return unique.length > limit;
};
I tested several of the solutions in this thread, and you can find the result here. If there are only a few unique values, the method by using arrays is the fastest, but if there are many unique values it quickly becomes the slowest, and on large arrays slowest by several magnitudes.
More profiling
I did some more tests with node v12.10.0. The results are normalized after the fastest method for each test.
Worst case scenario: 1000000 entries, all unique:
Set 1.00 // See this answer
Map 1.26 // See answer by Nikhil
Reduce 1.44 // See answer by Bali Balo
Array Infinity // See this answer
Best case scenario: 1000000 entries, all the same:
Array 1.00
Set 1.16
Map 2.60
Reduce 3.43
Question test case: [1, 1, 2, 1, 1, 3, 1, 1, 4, 1]
Array 1.00
Map 1.29
Set 1.47
Reduce 4.25
Another test case: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,
1,1,1,1,1,1,1,3,4,1,1,1,1,1,1,1,2,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,5 ]
Array 1.00
Set 1.13
Map 2.24
Reduce 2.39
Conclusion
The method that uses Set works for both small and large arrays, and performs well regardless of if there are many unique values or not. The version that are using arrays can be faster if there are few unique values, but quickly becomes very slow if there are many unique values.
Using sets, We count hypothetical unique set size and duplicateSet size and delete unique set element for each duplicate found. If unique set size goes below n, we stop iterating.
function uniqueGtN(res, n) {
let uniqSet = new Set(res);
let max = uniqSet.size;
if (max <= n) return false;
let dupSet = new Set();
return !res.some(e => {
if (dupSet.has(e)) {
if (uniqSet.has(e)) {
uniqSet.delete(e);
console.log(...uniqSet);
return (--max <= n);
}
} else {
dupSet.add(e);
}
});
}
console.log(uniqueGtN([1, 1, 2, 1, 1, 3, 3, 1], 2));
From your original solution, I have changed few things, it seems to be working fine:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => {
let firstIndex = s.indexOf(e);
let lastIndex = s.lastIndexOf(e);
// NOT unique
if (firstIndex != lastIndex) {
return false;
}
// unique
return e > n;
});
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
So the shorter version looks like this:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => s.indexOf(e) != s.lastIndexOf(e) ? false : e > n);
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
The code listed in your question does not work because m is not shared across the calls to the some callback function. It is a parameter, and its value is 2 at each iteration.
To fix this, either put m outside, or use the thisArg of the some function (but that means you can't use an arrow function)
let m = 2;
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some((n,i,s) => i > 0 && n == s[i-1] ? !(m--) : false);
// ----- or -----
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some(function(n,i,s) { return i > 0 && n == s[i-1] ? !(this.m--) : false; }, { m: 2 });
Note: this code seems to count if the number of duplicates exceeds a certain value, not the number of unique values.
As another side note, I know you mentioned you did not want to use a duplicate removal algorithm, but performant ones (for example hash-based) would result in something close to O(n).
Here is a solution to count all the values appearing exactly once in the initial array. It is a bit obfuscated and hard to read, but you seem to be wanting something concise. It is the most performant I can think of, using 2 objects to store values seen at least once and the ones seen multiple times:
let res = [1,1,2,3,4].reduce((l, e) => (l[+!l[1][e]][e] = true, l), [{},{}]).map(o => Object.keys(o).length).reduce((more,once) => once-more) > 2;
Here is the less minified version for people who don't like the short version:
let array = [1,1,2,3,4];
let counts = array.reduce((counts, element) => {
if (!counts.atLeastOne[element]) {
counts.atLeastOne[element] = true;
} else {
counts.moreThanOne[element] = true;
}
return counts;
}, { atLeastOne: {}, moreThanOne: {} });
let exactlyOnceCount = Object.keys(counts.atLeastOne).length - Object.keys(counts.moreThanOne).length;
let isOverLimit = exactlyOnceCount > 2;
Whenever I have a type of problem like this, I always like to peek at how the underscore JS folks have done it.
[Ed again: removed _.countBy as it isn't relevant to the answer]
Use the _.uniq function to return a list of unique values in the array:
var u = _.uniq([1,1,2,2,2,3,4,5,5]); // [1,2,3,4,5]
if (u.length > n) { ...};
[ed:] Here's how we might use that implementation to write our own, opposite function that returns only non-unique collection items
function nonUnique(array) {
var result = [];
var seen = [];
for (var i = 0, length = array.length; i < length; i++) {
var value = array[i];
if (seen.indexOf(value) === -1) { // warning! naive assumption
seen.push(value);
} else {
result.push(value);
}
}
console.log("non-unique result", result);
return result;
};
function hasMoreThanNUnique(array, threshold) {
var uArr = nonUnique(array);
var accum = 0;
for (var i = 0; i < array.length; i++) {
var val = array[i];
if (uArr.indexOf(val) === -1) {
accum++;
}
if (accum > threshold) return true;
}
return false;
}
var testArrA = [1, 1, 2, 2, 2, 3, 4, 5]; // unique values: [3, 4, 5]
var testArrB = [1, 1, 1, 1, 4]; // [4]
var testResultsA = hasMoreThanNUnique(testArrA, 3)
console.log("testArrA and results", testResultsA);
var testResultsB = hasMoreThanNUnique(testArrB, 3);
console.log("testArrB and results", testResultsB);
So far, I came up with the following:
const countNum = [1,1,1,2,1,1,3,1,1,1,4,1,1].reduce((r,n) => (r[n]=(r[n]||0)+1, r), {});
const res = Object.entries(countNum).some(([n,q]) => q == 1 ? !(m--) : false, m=2);
console.log(res);
.as-console-wrapper{min-height:100%}
But I don't really like array->object->array conversion about that. Is there a faster and (at the same time compact) solution?
Related
A job posting wants me to write a an answer to a question which if I solve I am eligible for the next rownd.
Write a function that will return the array combination with value
summed at 5. Important: Use only one "for" loop. Example: var
rand_array = [1,3,5,2,4,6]; var target_sum = 5; Output = [1,4], [5],
[3,2], [2,3], [4,1];
I attempted to find a solution online and stumbled upon this:
https://www.geeksforgeeks.org/given-an-array-a-and-a-number-x-check-for-pair-in-a-with-sum-as-x/ as StackOverflow wants you to do your own research first.
However, when trying to convert it to JS, all that happened was that it returned just one case where it worked. I need it to return every case where it worked. I then make some other changes and it just stopped working now.
var ra = [1,3,5,2,4,6];
var target = 5
ra.sort();
lower = 0;
higher = ra.length -1;
var solutions = [];
var result;
while (lower < higher) {
if (ra[lower] + ra[higher] === target){
result = [ra[lower], ra[higher]];
solutions.push(result);
}
else if (ra[lower] + ra[higher] > target){
higher--;
}
else {
lower++;
}
}
return solutions;
}
console.log(solutions);
Can someone write an example for me?
Your code does not work at all at the moment because it doesn't always increment lower or higher (resulting in an infinite loop). It also has greater complexity than necessary (.sort has complexity O(n log n)), but the instructions indicate that low complexity is important. The array also isn't being sorted numerically. (To sort numerically, use .sort((a, b) => a - b))
If you want a solution with the least complexity possible, O(n), while iterating, create an object. On every iteration, check to see if the object has a key for which the current number would sum with to 5 (eg, when iterating on 1, look to see if a 4 property exists on the object). If one is found, add it to the solutions. Otherwise, set a new key on the object:
const ra = [1, 3, 5, 2, 4, 6];
const target = 5;
const solutions = [];
const obj = {};
for (const item of ra) {
const match = target - item;
if (obj[match]) {
solutions.push([item, match]);
delete obj[match];
} else {
obj[item] = true;
}
}
console.log(solutions);
If there may be repeated numbers, then store a count in the object instead of just true:
const ra = [1, 1, 1, 3, 5, 2, 4, 6, 4, 4];
const target = 5;
const solutions = [];
const obj = {};
for (const item of ra) {
const match = target - item;
if (obj[match]) {
solutions.push([item, match]);
obj[match]--;
} else {
obj[item] = (obj[item] || 0) + 1;
}
}
console.log(solutions);
I don't want to write the actual answer because its a job assignment, but I will say that a simple 2 loop function is the obvies solution, than try to think not checking the array from the top and the bottum, rether like the formation of a loop in a loop.
hint :
let i = 0;
let j = 0;
while (i < arr.langth) {
...
if (j < arr.langth) {
j++;
} else {
j = 0;
i++;
}
}
Your code as it stands just does not work at all. CertainPerformance has a solid answer, except that it doesn't do the task as required, i.e. treat the same numbers in a different order as different or get the values which are equal to target as solutions. Here is my solution to your problem:
const ra = [1,3,5,2,4,6];
const target = 5
function getSumsOfTarget(ra, target){
ra.sort();
lower = 0;
higher = ra.length -1;
const solutions = [];
let result;
while (lower < ra.length && higher >= 0) {
const sum = ra[lower] + ra[higher];
if (ra[lower] === target) {
result = [ra[lower]];
solutions.push(result);
break;
}
if (sum === target){
result = [ra[lower], ra[higher]];
solutions.push(result);
lower++;
}
else if (sum > target){
higher--;
}
else {
lower++;
}
}
return solutions;
}
console.log(getSumsOfTarget(ra, target));
I have been trying to make a excercise in the course I am taking. At the end, I did what was asked, but I personally think I overdid too much and the output is not convenient -- it's a nested array with some blank arrays inside...
I tried to play with return, but then figured out the problem was in the function I used: map always returns an array. But all other functions, which are acceptable for arrays (in paticular forEach and I even tried filter) are not giving the output at all, only undefined. So, in the end, I have to ask you how to make code more clean with normal output like array with just 2 needed numbers in it (I can only think of complex way to fix this and it'll add unneeded junk to the code).
Information
Task:
Write a javascript function that takes an array of numbers and a target number. The function should find two different numbers in the array that, when added together, give the target number. For example: answer([1,2,3], 4) should return [1,3]
Code
const array1 = [1, 2, 3];
const easierArray = [1, 3, 5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = ((arr, targetNum) => {
const correctNumbers = arr.map((num, index) => {
let firstNumber = num;
// console.log('num',num,'index',index);
const arrayWeNeed = arr.filter((sub_num, sub_index) => {
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum) {
const passableArray = [firstNumber, sub_num] //aka first and second numbers that give the targetNum
return sub_num; //passableArray gives the same output for some reason,it doesn't really matter.
}
})
return arrayWeNeed
})
return correctNumbers;
// return `there is no such numbers,that give ${targetNum}`;
})
console.log(findTwoPartsOfTheNumber(easierArray, 8));
console.log(findTwoPartsOfTheNumber(array1, 4));
Output
[[],[5],[3]]
for the first one
You can clean up the outpu by flatting the returned arrays :
return arrayWeNeed.flat();
and
return correctNumbers.flat();
const array1 = [1, 2, 3];
const easierArray = [1, 3, 5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = ((arr, targetNum) => {
const correctNumbers = arr.map((num, index) => {
let firstNumber = num;
// console.log('num',num,'index',index);
const arrayWeNeed = arr.filter((sub_num, sub_index) => {
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum) {
const passableArray = [firstNumber, sub_num] //aka first and second numbers that give the targetNum
return sub_num; //passableArray gives the same output for some reason,it doesn't really matter.
}
})
return arrayWeNeed.flat();
})
return correctNumbers.flat();
// return `there is no such numbers,that give ${targetNum}`;
})
console.log(findTwoPartsOfTheNumber(easierArray, 8));
console.log(findTwoPartsOfTheNumber(array1, 4));
However, using a recursive function could be simpler :
const answer = (arr, num) => {
if (arr.length < 1) return;
const [first, ...rest] = arr.sort();
for (let i = 0; i < rest.length; i++) {
if (first + rest[i] === num) return [first, rest[i]];
}
return answer(rest, num);
};
console.log(answer([1, 2, 3], 4));
console.log(answer([1, 3, 5], 8));
It looks like you are trying to leave .map() and .filter() beforehand, which you can't (without throwing an error). So I suggest a normal for approach for this kind of implementation:
const array1 = [1,2,3];
const easierArray = [1,3,5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = (arr,targetNum) =>{
for(let index = 0; index < arr.length; index++) {
let firstNumber = arr[index];
// console.log('num',num,'index',index);
for(let sub_index = 0; sub_index < arr.length; sub_index++){
const sub_num = arr[sub_index];
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum){
const passableArray = [firstNumber,sub_num]//aka first and second numbers that give the targetNum
return passableArray; //passableArray gives the same output for some reason,it doesn't really matter.
}
}
}
return `there is no such numbers,that give ${targetNum}`;
}
console.log(findTwoPartsOfTheNumber(easierArray,8));
console.log(findTwoPartsOfTheNumber(array1,4));
console.log(findTwoPartsOfTheNumber(array1,10));
I've just grab your code and changed map and filter to for implementation.
There doesn't appear to be any requirement for using specific array functions (map, forEach, filter, etc) in the problem statement you listed, so the code can be greatly simplified by using a while loop and the fact that you know that the second number has to be equal to target - first (since the requirement is first + second == target that means second == target - first). The problem statement also doesn't say what to do if no numbers are found, so you could either return an empty array or some other value, or even throw an error.
const answer = (list, target) => {
while (list.length > 0) { // Loop until the list no longer has any items
let first = list.shift() // Take the first number from the list
let second = target - first // Calculate what the second number should be
if (list.includes(second)) { // Check to see if the second number is in the remaining list
return [first, second] // If it is, we're done -- return them
}
}
return "No valid numbers found" // We made it through the entire list without finding a match
}
console.log(answer([1,2,3], 3))
console.log(answer([1,2,3], 4))
console.log(answer([1,2,3], 7))
You can also add all the values in the array to find the total, and subtract the total by the target to find the value you need to remove from the array. That will then give you an array with values that add up to the total.
let arr1 = [1, 3, 5]
const target = 6
const example = (arr, target) => {
let total = arr.reduce((num1, num2) => {
return num1 + num2
})
total = total - target
const index = arr.indexOf(total)
if (index > -1) {
return arr.filter(item => item !== total)
}
}
console.log(example(arr1, target))
Map and filter are nice functions to have if you know that you need to loop into the whole array. In your case this is not necessary.
So you know you need to find two numbers, let's say X,Y, which belong to an array A and once added will give you the target number T.
Since it's an exercise, I don't want to give you the working code, but here is a few hints:
If you know X, Y must be T - X. So you need to verify that T - X exists in your array.
array.indexOf() give you the position of an element in an array, otherwise -1
If X and Y are the same number, you need to ensure that their index are not the same, otherwise you'll return X twice
Returning the solution should be simple as return [X,Y]
So this can be simplified with a for (let i = 0; i < arr.length; i++) loop and a if statement with a return inside if the solution exist. This way, if a solution is found, the function won't loop further.
After that loop, you return [] because no solution were found.
EDIT:
Since you want a solution with map and filter:
findTwoPartsOfTheNumber = (arr, tNumber) => {
let solution = [];
arr.map((X, indexOfX) => {
const results = arr.filter((Y, indexOfY) => {
const add = Y + X
if (tNumber === add && indexOfX != indexOfY) return true;
else return false;
});
if (results > 0) solution = [X, results[0]];
})
return solution;
}
I'm working on a small algorithm to find the closest values of a given number in an random array of numbers. In my case I'm trying to detect connected machines identified by a 6-digit number ID ("123456", "0078965", ...) but it can be useful for example to find the closest geolocated users around me.
What I need is to list the 5 closest machines, no matter if their IDs are higher or lower. This code works perfectly but I'm looking for a smarter and better way to proceed, amha I got to much loops and arrays.
let n = 0; // counter
let m = 5; // number of final machines to find
// list of IDs founded (unordered: we can't decide)
const arr = ["087965","258369","885974","0078965","457896","998120","698745","399710","357984","698745","789456"]
let NUM = "176789" // the random NUM to test
const temp = [];
const diff = {};
let result = null;
// list the [m] highest founded (5 IDs)
for(i=0 ; i<arr.length; i++) {
if(arr[i] > NUM) {
for(j=0 ; j<m; j++) {
temp.push(arr[i+j]);
} break;
}
}
// list the [m] lowest founded (5 IDs)
for(i=arr.length ; i>=0; i--) {
if(arr[i] < NUM) {
for(j=m ; j>=0; j--) {
temp.push(arr[i-j]);
} break;
}
}
// now we are certain to get at least 5 IDs even if NUM is 999999 or 000000
temp.sort(function(a, b){return a - b}); // increase order
for(i=0 ; i<(m*2); i++) {
let difference = Math.abs(NUM - temp[i]);
diff[difference] = temp[i]; // [ 20519 : "964223" ]
}
// we now get a 10-values "temp" array ordered by difference
// list the [m] first IDs:
for(key in diff){
if(n < m){
let add = 6-diff[key].toString().length;
let zer = '0'.repeat(add);
let id = zer+diff[key]; // "5802" -> "005802"
result += (n+1)+":"+ id +" ";
n+=1;
}
}
alert(result);
-> "1:0078965 2:087965 3:258369 4:357984 5:399710" for "176789"
You actually don't need to have so many different iterations. All you need is to loop twice:
The first iteration attempt is to use .map() to create an array of objects that stores the ID and the absolute difference between the ID and num
The second iteration attempt is simply to use .sort() through the array of objects created in step 1, ranking them from lowest to highest difference
Once the second iteration is done, you simply use .slice(0, 5) to get the first 5 objects in the array, which now contains the smallest 5 diffs. Iterate through it again if you want to simply extract the ID:
const arr = ["087965","258369","885974","078965","457896","998120","698745","399710","357984","698745","789456"];
let num = "176789";
let m = 5; // number of final machines to find
// Create an array of objects storing the original arr + diff from `num`
const diff = arr.map(item => {
return { id: item, diff: Math.abs(+item - +num) };
});
// Sort by difference from `num` (lowest to highest)
diff.sort((a, b) => a.diff - b.diff);
// Get the first m entries
const filteredArr = diff.slice(0, m).map(item => item.id).sort();
// Log
console.log(filteredArr);
// Completely optional, if you want to format it the way you have in your question
console.log(`"${filteredArr.map((v, i) => i + ": " + v).join(', ')}" for "${num}"`);
You could take an array as result set, fill it with the first n elements and sort it by the delta of the wanted value.
For all other elements check if the absolute delta of the actual item and the value is smaller then the last value of the result set and replace this value with the actual item. Sort again. Repeat until all elements are processed.
The result set is ordered by the smallest delta to the greatest by using the target value.
const
absDelta = (a, b) => Math.abs(a - b),
sortDelta = v => (a, b) => absDelta(a, v) - absDelta(b, v),
array = [087965, 258369, 885974, 0078965, 457896, 998120, 698745, 399710, 357984, 698745, 789456],
value = 176789,
n = 5,
result = array.reduce((r, v) => {
if (r.length < n) {
r.push(v);
r.sort(sortDelta(value));
return r;
}
if (absDelta(v, value) < absDelta(r[n - 1], value)) {
r[n - 1] = v;
r.sort(sortDelta(value));
}
return r;
}, []);
console.log(result); // sorted by closest value
A few good approaches so far, but I can't resist throwing in another.
This tests a sliding window of n elements in a sorted version of the array, and returns the one whose midpoint is closest to the value you're looking for. This is a pretty efficient approach (one sort of the array, and then a single pass through that) -- though it does not catch cases where there's more than one correct answer (see the last test case below).
const closestN = function(n, target, arr) {
// make sure we're not comparing strings, then sort:
let sorted = arr.map(Number).sort((a, b) => a - b);
target = Number(target);
let bestDiff = Infinity; // actual diff can be assumed to be lower than this
let bestSlice = 0; // until proven otherwise
for (var i = 0; i <= sorted.length - n; i++) {
let median = medianOf(sorted[i], sorted[i+n-1]) // midpoint of the group
let diff = Math.abs(target - median); // distance to the target
if (diff < bestDiff) { // we improved on the previous attempt
bestDiff = diff; // capture this for later comparisons
bestSlice = i;
}
// TODO handle diff == bestDiff? i.e. more than one possible correct answer
}
return sorted.slice(bestSlice, bestSlice + n)
}
// I cheated a bit here; this won't work if a > b:
const medianOf = function(a, b) {
return (Math.abs(b-a) / 2) + a
}
console.log(closestN(5, 176789, ["087965", "258369", "885974", "0078965", "457896", "998120", "698745", "399710", "357984", "698745", "789456"]))
// more test cases
console.log(closestN(3, 5, [1,2,5,8,9])) // should be 2,5,8
console.log(closestN(3, 4, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(1, 4, [1,2,5,8,9])) // should be 5
console.log(closestN(3, 99, [1,2,5,8,9])) // should be 5,8,9
console.log(closestN(3, -99, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(3, -2, [-10, -5, 0, 4])) // should be -5, 0, 4
console.log(closestN(1, 2, [1,3])) // either 1 or 3 would be correct...
I have an array like [1,4,3,1,6,5,1,4,4]
Here Highest element frequency is 3 ,I need to select all elements from array that have 3 frequency like [1,4] in above example.
I have tried with this
var count = {},array=[1,4,3,1,6,5,1,4,4],
value;
for (var i = 0; i < array.length; i++) {
value = array[i];
if (value in count) {
count[value]++;
} else {
count[value] = 1;
}
}
console.log(count);
this will output array element with their frequency , now i need all elements that have highest frequency.
I'd approach this problem as follows.
First, write down how you think the problem can be solved IN ENGLISH, or something close to English (or your native language of course!). Write down each step. Start off with a high-level version, such as:
Count the frequency of each element in the input.
Find the highest frequency.
and so on. At this point, it's important that you don't get bogged down in implementation details. Your solution should be applicable to almost any programming language.
Next flesh out each step by adding substeps. For instance, you might write:
Find the highest frequency.
a. Assume the highest frequency is zero.
b. Examine each frequency. If it is higher than the current highest frqeuency, make it the current highest frequency.
Test your algorithm by executing it manually in your head.
Next, convert what you have written about into what is sometimes called pseudo-code. It is at this point that our algorithm starts to look a little bit like a computer program, but is still easily human-readable. We may now use variables to represent things. For instance, we could write "max_freq ← cur_freq". We can refer to arrays, and write loops.
Finally, convert your pseudo-code into JS. If all goes well, it should work the first time around!
In recent years, a lot of people are jumping right into JavaScript, without any exposure to how to think about algorithms, even simple ones. They imagine that somehow they need to be able to, or will magically get to the point where they can, conjure up JS out of thin air, like someone speaking in tongues. In fact, the best programmers do not instantly start writing array.reduce when confronted with a problem; they always go through the process--even if only in their heads--of thinking about the approach to the problem, and this is an approach well worth learning from.
If you do not acquire this skill, you will spend the rest of your career posting to SO each time you can't bend your mind around a problem.
A proposal with Array.prototype.reduce() for a temporary object count, Object.keys() for getting the keys of the temporary object, a Array.prototype.sort() method for ordering the count results and Array.prototype.filter() for getting only the top values with the most count.
Edit: Kudos #Xotic750, now the original values are returned.
var array = [1, 4, 3, 1, 6, 5, 1, 4, 4],
result = function () {
var temp = array.reduce(function (r, a, i) {
r[a] = r[a] || { count: 0, value: a };
r[a].count++;
return r;
}, {});
return Object.keys(temp).sort(function (a, b) {
return temp[b].count - temp[a].count;
}).filter(function (a, _, aa) {
return temp[aa[0]].count === temp[a].count;
}).map(function (a) {
return temp[a].value;
});
}();
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Bonus with a different attempt
var array = [1, 4, 3, 1, 6, 5, 1, 4, 4],
result = array.reduce(function (r, a) {
r.some(function (b, i) {
var p = b.indexOf(a);
if (~p) {
b.splice(p, 1);
r[i + 1] = r[i + 1] || [];
r[i + 1].push(a);
return true;
}
}) || (
r[1] = r[1] || [],
r[1].push(a)
);
return r;
}, []).pop();
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
you can try this
var input = [1,4,3,1,6,5,1,4,4];
var output = {};
for ( var counter = 0; counter < input.length; counter++ )
{
if ( !output[ input[ counter ] ] )
{
output[ input[ counter ] ] = 0;
}
output[ input[ counter ] ]++;
}
var outputArr = [];
for (var key in output)
{
outputArr.push([key, output[key]])
}
outputArr = outputArr.sort(function(a, b) {return b[1] - a[1]})
now initial values of outputArr are the ones with highest frequency
Here is the fiddle
Check this updated fiddle (this will give the output you want)
var input = [1,4,3,1,6,5,1,4,4];
var output = {}; // this object holds the frequency of each value
for ( var counter = 0; counter < input.length; counter++ )
{
if ( !output[ input[ counter ] ] )
{
output[ input[ counter ] ] = 0; //initialized to 0 if value doesn't exists
}
output[ input[ counter ] ]++; //increment the value with each occurence
}
var outputArr = [];
var maxValue = 0;
for (var key in output)
{
if ( output[key] > maxValue )
{
maxValue = output[key]; //find out the max value
}
outputArr.push([key, output[key]])
}
var finalArr = []; //this array holds only those keys whose value is same as the highest value
for ( var counter = 0; counter < outputArr.length; counter++ )
{
if ( outputArr[ counter ][ 1 ] == maxValue )
{
finalArr.push( outputArr[ counter ][ 0 ] )
}
}
console.log( finalArr );
I would do something like this. It's not tested, but it's commented for helping you to understand my approach.
// Declare your array
var initial_array = [1,4,3,1,6,5,1,4,4];
// Declare an auxiliar counter
var counter = {};
// Loop over the array
initial_array.forEach(function(item){
// If the elements is already in counter, we increment the repetition counter.
if counter.hasOwnProperty(item){
counter[item] += 1;
// If the element is not in counter, we set the repetitions to one
}else{
counter[item] = 1;
}
});
// counter = {1 : 3, 4 : 3, 3 : 1, 6 : 1, 5 : 1}
// We move the object keys to an array (sorting it's more easy this way)
var sortable = [];
for (var element in counter)
sortable.push([element, counter[element]]);
// sortable = [ [1,3], [4,3], [3,1], [6,1], [5,1] ]
// Sort the list
sortable.sort(function(a, b) {return a[1] - b[1]})
// sortable = [ [1,3], [4,3], [3,1], [6,1], [5,1] ] sorted, in this case both are equals
// The elements in the firsts positions are the elements that you are looking for
// This auxiliar variable will help you to decide the biggest frequency (not the elements with it)
higgest = 0;
// Here you will append the results
results = [];
// You loop over the sorted list starting for the elements with more frequency
sortable.forEach(function(item){
// this condition works because we have sorted the list previously.
if(item[1] >= higgest){
higgest = item[1];
results.push(item[0]);
}
});
I'm very much with what #torazaburo had to say.
I'm also becoming a fan of ES6 as it creeps more and more into my daily browser. So, here is a solution using ES6 that is working in my browser now.
The shims are loaded to fix browser browser bugs and deficiencies, which is recommended in all environments.
'use strict';
// Your array of values.
const array = [1, 4, 3, 1, 6, 5, 1, 4, 4];
// An ES6 Map, for counting the frequencies of your values.
// Capable of distinguishing all unique values except `+0` and `-0`
// i.e. SameValueZero (see ES6 specification for explanation)
const frequencies = new Map();
// Loop through all the `values` of `array`
for (let item of array) {
// If item exists in frequencies increment the count or set the count to `1`
frequencies.set(item, frequencies.has(item) ? frequencies.get(item) + 1 : 1);
}
// Array to group the frequencies into list of `values`
const groups = [];
// Loop through the frequencies
for (let item of frequencies) {
// The `key` of the `entries` iterator is the value
const value = item[0];
// The `value` of the `entries` iterator is the frequency
const frequency = item[1];
// If the group exists then append the `value`,
// otherwise add a new group containing `value`
if (groups[frequency]) {
groups[frequency].push(value);
} else {
groups[frequency] = [value];
}
}
// The most frequent values are the last item of `groups`
const mostFrequent = groups.pop();
document.getElementById('out').textContent = JSON.stringify(mostFrequent);
console.log(mostFrequent);
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.4.1/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.34.0/es6-shim.js"></script>
<pre id="out"></pre>
you can do like this to find count occurrence each number
var array = [1, 4, 3, 1, 6, 5, 1, 4, 4];
var frequency = array.reduce(function(sum, num) {
if (sum[num]) {
sum[num] = sum[num] + 1;
} else {
sum[num] = 1;
}
return sum;
}, {});
console.log(frequency)
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
Say, I have an array that looks like this:
var playlist = [
{artist:"Herbie Hancock", title:"Thrust"},
{artist:"Lalo Schifrin", title:"Shifting Gears"},
{artist:"Faze-O", title:"Riding High"}
];
How can I move an element to another position?
I want to move for example, {artist:"Lalo Schifrin", title:"Shifting Gears"} to the end.
I tried using splice, like this:
var tmp = playlist.splice(2,1);
playlist.splice(2,0,tmp);
But it doesn't work.
The syntax of Array.splice is:
yourArray.splice(index, howmany, element1, /*.....,*/ elementX);
Where:
index is the position in the array you want to start removing elements from
howmany is how many elements you want to remove from index
element1, ..., elementX are elements you want inserted from position index.
This means that splice() can be used to remove elements, add elements, or replace elements in an array, depending on the arguments you pass.
Note that it returns an array of the removed elements.
Something nice and generic would be:
Array.prototype.move = function (from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
Then just use:
var ar = [1,2,3,4,5];
ar.move(0,3);
alert(ar) // 2,3,4,1,5
Diagram:
If you know the indexes you could easily swap the elements, with a simple function like this:
function swapElement(array, indexA, indexB) {
var tmp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = tmp;
}
swapElement(playlist, 1, 2);
// [{"artist":"Herbie Hancock","title":"Thrust"},
// {"artist":"Faze-O","title":"Riding High"},
// {"artist":"Lalo Schifrin","title":"Shifting Gears"}]
Array indexes are just properties of the array object, so you can swap its values.
With ES6 you can do something like this:
const swapPositions = (array, a ,b) => {
[array[a], array[b]] = [array[b], array[a]]
}
let array = [1,2,3,4,5];
swapPositions(array,0,1);
/// => [2, 1, 3, 4, 5]
Here is an immutable version for those who are interested:
function immutableMove(arr, from, to) {
return arr.reduce((prev, current, idx, self) => {
if (from === to) {
prev.push(current);
}
if (idx === from) {
return prev;
}
if (from < to) {
prev.push(current);
}
if (idx === to) {
prev.push(self[from]);
}
if (from > to) {
prev.push(current);
}
return prev;
}, []);
}
You could always use the sort method, if you don't know where the record is at present:
playlist.sort(function (a, b) {
return a.artist == "Lalo Schifrin"
? 1 // Move it down the list
: 0; // Keep it the same
});
Change 2 to 1 as the first parameter in the splice call when removing the element:
var tmp = playlist.splice(1, 1);
playlist.splice(2, 0, tmp[0]);
Immutable version, no side effects (doesn’t mutate original array):
const testArr = [1, 2, 3, 4, 5];
function move(from, to, arr) {
const newArr = [...arr];
const item = newArr.splice(from, 1)[0];
newArr.splice(to, 0, item);
return newArr;
}
console.log(move(3, 1, testArr));
// [1, 4, 2, 3, 5]
codepen: https://codepen.io/mliq/pen/KKNyJZr
EDIT: Please check out Andy's answer as his answer came first and this is solely an extension of his
I know this is an old question, but I think it's worth it to include Array.prototype.sort().
Here's an example from MDN along with the link
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
return a - b;
});
console.log(numbers);
// [1, 2, 3, 4, 5]
Luckily it doesn't only work with numbers:
arr.sort([compareFunction])
compareFunction
Specifies a function that defines the sort order. If omitted, the array is sorted according to each character's Unicode code point value, according to the string conversion of each element.
I noticed that you're ordering them by first name:
let playlist = [
{artist:"Herbie Hancock", title:"Thrust"},
{artist:"Lalo Schifrin", title:"Shifting Gears"},
{artist:"Faze-O", title:"Riding High"}
];
// sort by name
playlist.sort((a, b) => {
if(a.artist < b.artist) { return -1; }
if(a.artist > b.artist) { return 1; }
// else names must be equal
return 0;
});
note that if you wanted to order them by last name you would have to either have a key for both first_name & last_name or do some regex magic, which I can't do XD
Hope that helps :)
Try this:
playlist = playlist.concat(playlist.splice(1, 1));
If you only ever want to move one item from an arbitrary position to the end of the array, this should work:
function toEnd(list, position) {
list.push(list.splice(position, 1));
return list;
}
If you want to move multiple items from some arbitrary position to the end, you can do:
function toEnd(list, from, count) {
list.push.apply(list, list.splice(from, count));
return list;
}
If you want to move multiple items from some arbitrary position to some arbitrary position, try:
function move(list, from, count, to) {
var args = [from > to ? to : to - count, 0];
args.push.apply(args, list.splice(from, count));
list.splice.apply(list, args);
return list;
}
Time complexity of all answers is O(n^2) because had used twice spice. But O(n/2) is possible.
Most Perfomance Solution:
Array with n elements,
x is to, y is from
should be n >x && n > y
time complexity should be |y - x|. So its is number of elements that is between from and to.
bestcase: O(1); //ex: from:4 to:5
average : O(n/2)
worthcase : O(n) //ex: from:0 to:n
function reOrder(from,to,arr) {
if(from == to || from < 0 || to < 0 ) { return arr};
var moveNumber = arr[from];
if(from < to) {
for(var i =from; i< to; i++){
arr[i] = arr[i+1]
}
}
else{
for(var i = from; i > to; i--){
arr[i] = arr[i-1];
}
}
arr[to] = moveNumber;
return arr;
}
var arr = [0,1,2,3,4,5,6,7,8,9,10,11,12,13];
console.log(reOrder(3,7,arr));
As a simple mutable solution you can call splice twice in a row:
playlist.splice(playlist.length - 1, 1, ...playlist.splice(INDEX_TO_MOVE, 1))
On the other hand, a simple inmutable solution could use slice since this method returns a copy of a section from the original array without changing it:
const copy = [...playlist.slice(0, INDEX_TO_MOVE - 1), ...playlist.slice(INDEX_TO_MOVE), ...playlist.slice(INDEX_TO_MOVE - 1, INDEX_TO_MOVE)]
I came here looking for a rearranging complete array, I want something like I did below, but found most of the answers for moving only one element from position A to position B.
Hope my answer will help someone here
function reArrangeArray(firstIndex=0,arr){
var a = [];
var b = []
for(let i = 0; i<= (arr.length-1); i++){
if(i<firstIndex){
a.push(arr[i])
}else{
b.push(arr[i])
}
}
return b.concat(a)
}
const arrayToRearrange = [{name: 'A'},{name: 'B'},{name: 'C'},{name: 'D'},{name: 'E'}];
reArrangeArray(2,arrayToRearrange)
// Output
// [
// { name: 'C' },
// { name: 'D' },
// { name: 'E' },
// { name: 'A' },
// { name: 'B' }
// ]
Reorder its work This Way
var tmpOrder = playlist[oldIndex];
playlist.splice(oldIndex, 1);
playlist.splice(newIndex, 0, tmpOrder);
I hope this will work