Related
I have an array [1,1,1,1,2,3,4,5,5,6,7,8,8,8]
How can I get an array of the distinct duplicates [1,5,8] - each duplicate in the result only once, regardless of how many times it appears in the original array
my code:
var types = availControls.map(item => item.type);
var sorted_types = types.slice().sort();
availControlTypes = [];
for (var i = 0; i < sorted_types.length - 1, i++) {
if (sorted_types[i + 1] == sorted_types[i])
availControlTypes.push(sorted_types[i]);
}
This gets me the duplicates, but not unique.
This will do it
var input = [1, 1, 1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8];
let filterDuplicates = arr => [...new Set(arr.filter((item, index) => arr.indexOf(item) != index))]
console.log(filterDuplicates(input))
You need a for loop, with an object that will hold the number of times the number appeared in the array. When the count is already 1, we can add the item to the result. We should continue to increment the counter, so we won't add more than a single duplicate of the same number (although we can stop at 2).
function fn(arr) {
var counts = {};
var result = [];
var n;
for(var i = 0; i < arr.length; i++) {
n = arr[i]; // get the current number
if(counts[n] === 1) result.push(n); // if counts is exactly 1, we should add the number to results
counts[n] = (counts[n] || 0) +1; // increment the counter
}
return result;
}
var arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8];
var result = fn(arr);
console.log(result)
const dupes = arr =>
Array.from(
arr.reduce((acc, item) => {
acc.set(item, (acc.get(item) || 0) + 1);
return acc;
}, new Map())
)
.filter(x => x[1] > 1)
.map(x => x[0]);
const arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8];
console.log(dupes(arr));
// [ 1, 5, 8 ]
ES6 1 liner.
This is very similar to how we find unique values, except instead of filtering for 1st occurrences, we're filtering for 2nd occurrences.
let nthOccurrences = (a, n = 1) => a.filter((v, i) => a.filter((vv, ii) => vv === v && ii <= i).length === n);
let x = [1, 1, 1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8];
let uniques = nthOccurrences(x, 1);
let uniqueDuplicates = nthOccurrences(x, 2);
let uniqueTriplets = nthOccurrences(x, 3); // unique values with 3 or more occurrences
console.log(JSON.stringify(uniques));
console.log(JSON.stringify(uniqueDuplicates));
console.log(JSON.stringify(uniqueTriplets));
Okay so as the title says my goal is to find the least duplicate elements, given that the elements are only integers.
ex1: array = [1,1,2,2,3,3,3] result should be 1,2
ex2: array = [1,2,2,3,3,4] result should be 1,4
I could use the xor operator to find the elements that appear only once but since there might be only duplicates I cant.
I was thinking of first checking with XOR if the're any non-duplicate elements. If no proceed with fors to check for only two occurrences of the same element and so on, but that is not a good approach as its kinda slow,
any suggestions?
Another approach, using new Set() and few Array.prototype functions. If you have any questions, let me know.
var array1 = [1, 1, 2, 2, 3, 3, 3],
array2 = [1, 2, 2, 3, 3, 4];
function sort(arr) {
var filtered = [...new Set(arr)],
solution = [],
res = filtered.reduce(function(s, a) {
s.push(arr.filter(c => c == a).length);
return s;
}, []);
var minDupe = Math.min.apply([], res);
res.forEach((v, i) => v == minDupe ? solution.push(filtered[i]) : null)
console.log(solution)
}
sort(array1);
sort(array2);
Using Array#forEach instead of Array#reduce.
var array1 = [1, 1, 2, 2, 3, 3, 3],
array2 = [1, 2, 2, 3, 3, 4];
function sort(arr) {
var filtered = [...new Set(arr)],
solution = [],
res = [];
filtered.forEach(v => res.push(arr.filter(c => c == v).length));
var minDupe = Math.min.apply([], res);
res.forEach((v, i) => v == minDupe ? solution.push(filtered[i]) : null)
console.log(solution)
}
sort(array1);
sort(array2);
There may be a better or faster solution, but I would suggest to create a hash (object) with the integer as keys and the counts as values. You can create this in one loop of the array. Then, loop over the object keys keeping track of the minimum value found and add the key to the result array if it satisfies the minimum duplicate value.
Example implementation:
const counts = input.reduce((counts, num) => {
if (!counts.hasOwnProperty(num)) {
counts[num] = 1;
}
else {
counts[num]++;
}
return counts;
}, {});
let minimums = [];
let minCount = null;
for (const key in counts) {
if (!minimums.length || counts[key] < minCount) {
minimums = [+key];
minCount = counts[key];
}
else if (counts[key] === minCount) {
minimums.push(+key);
}
}
return minimums;
You can also simplify this a little bit using lodash: one operation to get the counts and another to get the minimum count and get the list of values that match that minimum count as a key:
import { countBy, invertBy, min, values } from "lodash";
const counts = countBy(input);
const minCount = min(values(counts));
return invertBy(counts)[minCount];
You could count the appearance, sort by count and delete all same max count keys. Then return the original values.
Steps:
declarate all variables, especial the hash object without any prototypes,
use the items as key got the hash table and if not set use an object with the original value and a count property,
increment count of actual hash,
get all keys from the hash table,
sort the keys in descending order of count,
get the count of the first element and store it in min,
filter all keys with min count,
get the original value for all remaining keys.
function getLeastDuplicateItems(array) {
var hash = Object.create(null), keys, min;
array.forEach(function (a) {
hash[a] = hash[a] || { value: a, count: 0 };
hash[a].count++;
});
keys = Object.keys(hash);
keys.sort(function (a, b) { return hash[a].count - hash[b].count; });
min = hash[keys[0]].count;
return keys.
filter(function (k) {
return hash[k].count === min;
}).
map(function (k) {
return hash[k].value;
});
}
var data = [
[1, 1, 2, 2, 3, 3, 3],
[1, 2, 2, 3, 3, 4],
[4, 4, 4, 6, 6, 4, 7, 8, 5, 5, 6, 3, 4, 6, 6, 7, 7, 8, 3, 3]
];
console.log(data.map(getLeastDuplicateItems));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A single loop solution with a variable for min and an array for collected count.
function getLeastDuplicateItems(array) {
var hash = Object.create(null),
temp = [],
min = 1;
array.forEach(function (a) {
var p = (temp[hash[a]] || []).indexOf(a);
hash[a] = (hash[a] || 0) + 1;
temp[hash[a]] = temp[hash[a]] || [];
temp[hash[a]].push(a);
if (min > hash[a]) {
min = hash[a];
}
if (p === -1) {
return;
}
temp[hash[a] - 1].splice(p, 1);
if (min === hash[a] - 1 && temp[hash[a] - 1].length === 0) {
min++;
}
}, []);
return temp[min];
}
var data = [
[1, 1, 2, 2, 3, 3, 3],
[1, 2, 2, 3, 3, 4],
[4, 4, 4, 6, 6, 4, 7, 8, 5, 5, 6, 3, 4, 6, 6, 7, 7, 8, 3, 3],
];
console.log(data.map(getLeastDuplicateItems));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Question has been moved to CodeReview: https://codereview.stackexchange.com/questions/154804/find-a-list-of-objects-in-an-array-with-javascript
Having an array of objects - such as numbers - what would be the most optimal (Memory and CPU efficiency) way if finding a sub group of objects? As an example:
demoArray = [1,2,3,4,5,6,7]
Finding [3,4,5] would return 2, while looking for 60 would return -1.
The function must allow for wrapping, so finding [6,7,1,2] would return 5
I have a current working solution, but I'd like to know if it could be optimized in any way.
var arr = [
1,
5,2,6,8,2,
3,4,3,10,9,
1,5,7,10,3,
5,6,2,3,8,
9,1]
var idx = -1
var group = []
var groupSize = 0
function findIndexOfGroup(g){
group = g
groupSize = g.length
var beginIndex = -2
while(beginIndex === -2){
beginIndex = get()
}
return beginIndex
}
function get(){
idx = arr.indexOf(group[0], idx+1);
if(idx === -1 || groupSize === 1){
return idx;
}
var prevIdx = idx
for(var i = 1; i < groupSize; i++){
idx++
if(arr[getIdx(idx)] !== group[i]){
idx = prevIdx
break
}
if(i === groupSize - 1){
return idx - groupSize + 1
}
}
return -2
}
function getIdx(idx){
if(idx >= arr.length){
return idx - arr.length
}
return idx
}
console.log(findIndexOfGroup([4,3,10])) // Normal
console.log(findIndexOfGroup([9,1,1,5])) // Wrapping
You could use the reminder operator % for keeping the index in the range of the array with a check for each element of the search array with Array#every.
function find(search, array) {
var index = array.indexOf(search[0]);
while (index !== -1) {
if (search.every(function (a, i) { return a === array[(index + i) % array.length]; })) {
return index;
}
index = array.indexOf(search[0], index + 1);
}
return -1;
}
console.log(find([3, 4, 5], [1, 2, 3, 4, 5, 6, 7])); // 2
console.log(find([6, 7, 1, 2], [1, 2, 3, 4, 5, 6, 7])); // 5
console.log(find([60], [1, 2, 3, 4, 5, 6, 7])); // -1
console.log(find([3, 4, 5], [1, 2, 3, 4, 6, 7, 3, 4, 5, 9])); // 6
.as-console-wrapper { max-height: 100% !important; top: 0; }
My take on the problem is to use slice() and compare each subarray of length equal to the group's length to the actual group array. Might take a bit long, but the code is short enough:
// The array to run the tests on
var arr = [
1,
5, 2, 6, 8, 2,
3, 4, 3, 10, 9,
1, 5, 7, 10, 3,
5, 6, 2, 3, 8,
9, 1
];
// Check arrays for equality, provided that both arrays are of the same length
function arraysEqual(array1, array2) {
for (var i = array1.length; i--;) {
if (array1[i] !== array2[i])
return false;
}
return true;
}
// Returns the first index of a subarray matching the given group of objects
function findIndexOfGroup(array, group) {
// Get the length of both arrays
var arrayLength = array.length;
var groupLength = group.length;
// Extend array to check for wrapping
array = array.concat(array);
var i = 0;
// Loop, slice, test, return if found
while (i < arrayLength) {
if (arraysEqual(array.slice(i, i + groupLength), group))
return i;
i++;
}
// No index found
return -1;
}
// Tests
console.log(findIndexOfGroup(arr,[4,3,10])); // Normal
console.log(findIndexOfGroup(arr,[9,1,1,5])); // Wrapping
console.log(findIndexOfGroup(arr,[9,2,1,5])); // Not found
If the group is longer than the array, some errors might occur, but I leave it up to you to extend the method to deal with such situations.
I have a number array [2, 1, 3, 4, 5, 1] and want to remove the smallest number in the list. But somehow my IF statement gets skipped.
I checked and by itself "numbers[i + 1]" and "numbers[i]" do work, but "numbers[i + 1] < numbers[i]" doesn't...
function removeSmallest(numbers) {
var smallestNumberKEY = 0;
for (i = 0; i <= numbers.lenths; i++) {
if (numbers[i + 1] < numbers[i]) {
smallestNumberKEY = i + 1;
}
}
numbers.splice(smallestNumberKEY, 1);
return numbers;
}
document.write(removeSmallest([2, 1, 3, 4, 5, 1]));
You have a typo in your code, array doesn't have lenths property
function removeSmallest(numbers) {
var smallestNumberKEY = 0;
for (var i = 0; i < numbers.length - 1; i++) {
if (numbers[i + 1] < numbers[i]) {
smallestNumberKEY = i + 1;
numbers.splice(smallestNumberKEY, 1);
}
}
return numbers;
}
document.write(removeSmallest([2, 1, 3, 4, 5, 1]));
But your algorithm wont work for another array, e.g [5, 3, 1, 4, 1], it will remove a value 3 too.
You can find the min value with Math.min function and then filter an array
function removeSmallest(arr) {
var min = Math.min(...arr);
return arr.filter(e => e != min);
}
You can use Array#filter instead
function removeSmallest(arr) {
var min = Math.min.apply(null, arr);
return arr.filter((e) => {return e != min});
}
console.log(removeSmallest([2, 1, 3, 4, 5, 1]))
Short one liner. If the smallest value exist multiple times it will only remove ONE. This may or may not be what you want.
const result = [6,1,3,1].sort().filter((_,i) => i) // result = [1,3,6]
It works by sorting and then creating a new array from the items where indeces are truthy(anything but 0)
another solution with splice and indexOf:
array = [2, 1, 3, 4, 5, 1];
function replace(arr){
arr = arr.slice(); //copy the array
arr.splice( arr.indexOf(Math.min.apply(null, arr)),1)
return arr;
}
document.write( replace(array) ,'<br> original array : ', array)
edit : making a copy of the array will avoid the original array from being modified
"Short" solution using Array.forEach and Array.splice methods:
function removeSmallest(numbers) {
var min = Math.min.apply(null, numbers);
numbers.forEach((v, k, arr) => v !== min || arr.splice(k,1));
return numbers;
}
console.log(removeSmallest([2, 1, 3, 4, 5, 1])); // [2, 3, 4, 5]
This is a proposal with a single loop of Array#reduce and without Math.min.
The algorithm sets in the first loop min with the value of the element and returns an empty array, because the actual element is the smallest value and the result set should not contain the smallest value.
The next loop can have
a value smaller than min, then assign a to min and return a copy of the original array until the previous element, because a new minimum is found and all other previous elements are greater than the actual value and belongs to the result array.
a value greater then min, then the actual value is pushed to the result set.
a value equal to min, then the vaue is skipped.
'use strict';
var removeSmallest = function () {
var min;
return function (r, a, i, aa) {
if (!i || a < min) {
min = a;
return aa.slice(0, i);
}
if (a > min) {
r.push(a);
}
return r;
}
}();
document.write('<pre>' + JSON.stringify([2, 1, 3, 2, 4, 5, 1].reduce(removeSmallest, []), 0, 4) + '</pre>');
I like this oneliner: list.filter(function(n) { return n != Math.min.apply( Math, list ) })
check it out here: https://jsfiddle.net/rz2n4rsd/1/
function remove_smallest(list) {
return list.filter(function(n) { return n != Math.min.apply( Math, list ) })
}
var list = [2, 1, 0, 4, 5, 1]
console.log(list) // [2, 1, 0, 4, 5, 1]
list = remove_smallest(list)
console.log(list) // [2, 1, 4, 5, 1]
list = remove_smallest(list)
console.log(list) // [2, 4, 5]
I had to do this but I needed a solution that did not mutate the input array numbers and ran in O(n) time. If that's what you're looking for, try this one:
const removeSmallest = (numbers) => {
const minValIndex = numbers.reduce((finalIndex, currentVal, currentIndex, array) => {
return array[currentIndex] <= array[finalIndex] ? currentIndex : finalIndex
}, 0)
return numbers.slice(0, minValIndex).concat(numbers.slice(minValIndex + 1))
}
function sumOfPaiars(ints){
var array = [];
var min = Math.min(...ints)
console.log(min)
for(var i=0;i<ints.length;i++){
if(ints[i]>min){
array.push(ints[i])
}
}
return array
}
If you only wish to remove a single instance of the smallest value (which was my use-case, not clear from the op).
arr.sort().shift()
Here is a piece of code that is work properly but is not accepted from codewars:
let numbers = [5, 3, 2, 1, 4];
numbers.sort(function numbers(a, b) {
return a - b;
});
const firstElement = numbers.shift();
I'm not sure if the title of this question is correct or not and also not sure what the appropriate keyword to search on google.
I have an array look like:
var myArray = [1,1,2,2,2,3,4,4,4];
and I want to sort my array into:
var myArray = [1,2,3,4,1,2,4,2,4];
Please in to my expected result. the order is ascending but duplicate value will repeated on last sequence instead of put it together in adjacent keys. So the expected result grouped as 1,2,3,4 1,2,4 and 2,4.
Thank you for your help and sorry for my bad English.
This code works. But it may exist a better solution.
// We assume myArray is already sorted
var myArray = [1,1,2,2,2,3,4,4,4],
result = [];
while (myArray.length) {
var value = myArray.shift();
// Find place
var index = 0;
while(result[index] && result[index][result[index].length - 1] == value) index++;
if(!result[index]) {
result[index] = [];
}
result[index][result[index].length] = value;
}
result.reduce(function(current, sum) {
return current.concat(sum);
});
console.log(result) // Display [1, 2, 3, 4, 1, 2, 4, 2, 4]
Here is my method using JQuery and it does not assume the array is already sorted.
It will iterate through the array and no duplicates to tempResultArray, once finished, it will then add them to the existing result and repeat the process again to find duplicates.
This is not the most efficient method, but it can be handled by one function and does not require the array to be sorted.
var myArray = [1,1,2,2,2,3,4,4,4],result = [];
while (myArray && myArray.length) {
myArray = customSort(myArray);
}
console.log(result);
function customSort(myArray){
var tempResultArray = [], tempMyArray = [];
$.each(myArray, function(i, el){
if($.inArray(el, tempResultArray ) === -1){
tempResultArray.push(el);
}else{
tempMyArray.push(el);
}
});
tempResultArray.sort(function(a, b){return a-b});
$.merge( result,tempResultArray)
return tempMyArray;
}
JSFiddle
This proposal features a straight forward approach with focus on array methods.
function sprout(array) {
return array.reduce(function (r, a) {
!r.some(function (b) {
if (b[b.length - 1] < a) {
b.push(a);
return true;
}
}) && r.push([a]);
return r;
}, []).reduce(function (r, a) {
return r.concat(a);
});
}
document.write('<pre>' + JSON.stringify(sprout([1, 1, 2, 2, 2, 3, 4, 4, 4]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sprout([1, 2, 3, 7, 7, 7]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sprout([1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 6, 6, 6, 7]), 0, 4) + '</pre>');
here's another solution:
var myArray = [1, 1, 2, 2, 2, 3, 4, 4, 4];
function mySequelArray(arr) {
var res = arguments[1] || [];
var nextVal;
var min = Math.min.apply(null, arr);
if (res.length > 0) {
nextVal = arr.filter(function (x) {
return x > res[res.length - 1]
}).sort()[0] || min;
} else {
nextVal = min;
}
res.push(nextVal);
arr.splice(arr.indexOf(nextVal), 1);
return (arr.length > 0) ? mySequelArray(arr, res) : res;
}
console.log(mySequelArray(myArray))
fiddle
My best approach will be to split your array into separate arrays for each repeated value, then arrange each separate array and join altogether.
UPDATED:
I wrote a quick code sample that should work if the same number in inputArray is not given more than twice. You could improve it by making it recursive thus creating new arrays for each new number and removing the limitation. Had some free time so i re-wrote a recursive function to sort any given array in sequence groups like you wanted. Works like a charm, inputArray does not need to be sorted and doesn't require any libraries. Jsfiddle here.
var inputArray = [3, 4, 1, 2, 3, 1, 2, 4, 1, 2, 5, 1];
var result = sortInSequence(inputArray);
console.log(result); //output: [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 1]
function sortInSequence(inputArray){
var inputArraySize = inputArray.length,
tempArray = [], //holds new array we are populating
sameValuesArray = [], //holds same values that we will pass as param in recursive call
rSorted = []; //init sorted array in case we have no same values
for(var i = inputArraySize; i > 0; i--){
var value = inputArray.pop();
tempArray.push(value);
var counter = 0,
tempArraySize = tempArray.length;
for(var j = 0; j < tempArraySize; j++){
if(tempArray[j] == value){
counter++;
}
}
if(counter == 2){
//value found twice, so remove it from tempArray and add it in sameValuesArray
var sameValue = tempArray.pop();
sameValuesArray.push(sameValue);
}
}
if(sameValuesArray.length > 0){
rSorted = sortInSequence(sameValuesArray);
}
tempArray.sort();
return tempArray.concat(rSorted);
}