This question already has answers here:
How to get the difference between two arrays in JavaScript?
(84 answers)
Closed 2 years ago.
I wrote the function to accomplish what was asked, but was wondering if there was a better and nicer way to do that?
function arrayDiff(a, b) {
let myArr = [];
for (let i = 0; i < a.length; i++) {
if (a[i] == b[0] || a[i] == b[1]) {
continue;
} else {
myArr.push(a[i]);
}
}
return myArr;
}
arrayDiff([1,2,3], [2,3]) //returns [1]
What if the length of the array "b" was more than just two elements, for example 5, how could I write a better code?
You could take a single comparison by using Array#includes.
function arrayDiff(a, b) {
let myArr = [];
for (let i = 0; i < a.length; i++) {
if (b.includes(a[i])) continue;
myArr.push(a[i]);
}
return myArr;
}
console.log(arrayDiff([1, 2, 3], [2, 3])); // [1]
Another faster approach takes an object with O(1) fo looking for the value of b, for example with a Set.
function arrayDiff(a, b) {
let myArr = [],
excluding = new Set(b);
for (let i = 0; i < a.length; i++) {
if (excluding.has(a[i])) continue;
myArr.push(a[i]);
}
return myArr;
}
console.log(arrayDiff([1, 2, 3], [2, 3])); // [1]
const a = [1,2,3], b = [2,3]
let result = a.filter(n=>!b.includes(n))
console.log(result)
You can loop the second array and get the index of the item from the first array. Then use splice to remove the item from first array
function arrayDiff(a, b) {
let z = a;
b.forEach((item) => {
const getIndx = z.indexOf(item);
if (getIndx !== -1) {
z.splice(getIndx, 1)
}
});
return z;
}
console.log(arrayDiff([1, 2, 3], [2, 3]))
I'm looking for an efficient solution to sort an array depending of how many times an element appears
For example :
let values = ["10", "4", "4", "4", "7", "7"]
I think the best output would be something like [{number, frequency}, ...], which, in the example would look like this :
[{4, 3}, {7, 2}, {10, 1}]
I've seen a lot of ways to do it, but every solution just sort the array depending of the frequency, without any access on how many times the element appears.
At the moment I only have this code that I got from another StackOverflow topic (can't remember which one sorry)
var map = values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
var newTypesArray = Object.keys(map).sort(function(a, b) {
return map[a] < map[b];
});
console.log(newTypesArray);
It's doing a great job at sorting depending on the frequency, but I can't access how many times an element is repeated. And I have no idea how to do it...
Any thoughts?
const arr = [1, 1, 1, 2, 2, 3];
// use reduce for that.
const result = arr.reduce((result, item) => {
const count = result[item];
if (count === undefined) {
result[item] = 1;
} else {
result[item] += 1;
}
return result;
}, {});
// You'll get the result similar to this: {[item]: [count]}
// And then you can transform it into entries array:
const entries = Object.entries(result);
// entries are [[item, count], ...];
// And then sort
const sorted = entries.sort((entryA, entryB) => entryA[1] - entryB[1]);
// You'll have ascending sorted array by count.
console.log(sorted);
Something like this might do the trick:
const data = ["10", "7", "5", "4", "4", "7", "7", "4", "5", "4"];
const result = data.reduce((r, c) => (r[c] = (r[c] || 0) + 1, r), {});
var keys=Object.keys(result).sort((a,b)=>result[b]-result[a]); // sorted keys
console.log(keys.map(k=>k+':'+result[k])); // output, ordered by keys
It is up to you how you want to return the results. result contains the count and keys the actual values.
You can use Object.entries() over the object you got from reduce now sorting the array of key-value pairs and finally mapping to the array of {number, frequency}:
let values = ["10", "4", "4", "4", "7", "7"]
var map = Object.entries(values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {}))
.sort((a, b) => a[1] < b[1])
.map((a) => ({[a[0]]:a[1]}));
console.log(map);
I think the best output would be something like [{number, frequency}, ...]
Actually these are not valid JavaScript objects inside your output array, if you want to show them as pairs you can use an array of arrays instead of this, the result will be like this:
[[number, frequency], ...]
In your code you were almost there, but you just need to map the result of your first reduce call to get the desired pair:
let values = ["10", "4", "4", "4", "7", "7"];
var map = values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
var newTypesArray = Object.keys(map).map(function(k) {
return [+k, map[k]];
}).sort((a,b) => b[1] - a[1]);
console.log(newTypesArray);
I need to construct a function intersection that compares input arrays and returns a new array with elements found in all of the inputs.
The following solution works if in each array the numbers only repeat once, otherwise it breaks. Also, I don't know how to simplify and not use messy for loops:
function intersection(arrayOfArrays) {
let joinedArray = [];
let reducedArray = [];
for (let iOuter in arrayOfArrays) {
for (let iInner in arrayOfArrays[iOuter]) {
joinedArray.push(arrayOfArrays[iOuter][iInner]);
}
return joinedArray;
}
for (let i in joinedArray.sort()) {
if (joinedArray[i] === joinedArray[ i - (arrayOfArrays.length - 1)]) {
reducedArray.push(joinedArray[i]);
}
}
return reducedArray;
}
Try thhis:-
function a1(ar,ar1){
x = new Set(ar)
y = new Set(ar1)
var result = []
for (let i of x){
if (y.has(i)){
result.push(i)
}
}
if (result){return result}
else{ return 0}
}
var a= [3,4,5,6]
var b = [8,5,6,1]
console.log(a1(a,b)) //output=> [5,6]
Hopefully this snippet will be useful
var a = [2, 3, 9];
var b = [2, 8, 9, 4, 1];
var c = [3, 4, 5, 1, 2, 1, 9];
var d = [1, 2]
function intersect() {
// create an empty array to store any input array,All the comparasion
// will be done against this one
var initialArray = [];
// Convert all the arguments object to array
// there can be n number of supplied input array
// sorting the array by it's length. the shortest array
//will have at least all the elements
var x = Array.prototype.slice.call(arguments).sort(function(a, b) {
return a.length - b.length
});
initialArray = x[0];
// loop over remaining array
for (var i = 1; i < x.length; i++) {
var tempArray = x[i];
// now check if every element of the initial array is present
// in rest of the arrays
initialArray.forEach(function(item, index) {
// if there is some element which is present in intial arrat but not in current array
// remove that eleemnt.
//because intersection requires element to present in all arrays
if (x[i].indexOf(item) === -1) {
initialArray.splice(index, 1)
}
})
}
return initialArray;
}
console.log(intersect(a, b, c, d))
There is a nice way of doing it using reduce to intersect through your array of arrays and then filter to make remaining values unique.
function intersection(arrayOfArrays) {
return arrayOfArrays
.reduce((acc,array,index) => { // Intersect arrays
if (index === 0)
return array;
return array.filter((value) => acc.includes(value));
}, [])
.filter((value, index, self) => self.indexOf(value) === index) // Make values unique
;
}
You can iterate through each array and count the frequency of occurrence of the number in an object where the key is the number in the array and its property being the array of occurrence in an array. Using the generated object find out the lowest frequency of each number and check if its value is more than zero and add that number to the result.
function intersection(arrayOfArrays) {
const frequency = arrayOfArrays.reduce((r, a, i) => {
a.forEach(v => {
if(!(v in r))
r[v] = Array.from({length:arrayOfArrays.length}).fill(0);
r[v][i] = r[v][i] + 1;
});
return r;
}, {});
return Object.keys(frequency).reduce((r,k) => {
const minCount = Math.min(...frequency[k]);
if(minCount) {
r = r.concat(Array.from({length: minCount}).fill(+k));
}
return r;
}, []);
}
console.log(intersection([[2,3, 45, 45, 5],[4,5,45, 45, 45, 6,7], [3, 7, 5,45, 45, 45, 45,7]]))
Newb here with a basic question. Here is my object:
{TestOne: 12, TestTwo: 6, TestThree: 4, TestFour: 2}
I've looped through it using a for-in loop and grabbed the properties and displayed it in my UI like so:
TestOne: 12
TestTwo: 6
TestThree: 4
TestFour: 2
I need to be able to display this by giving each item a numerical ranking (1,2,3,4...etc) and then displaying them by that ranking (corresponding to their actual order). In other words, what my users need to see on the screen is:
TestOne: 4
TestTwo: 3
TestThree: 2
TestFour: 1
Not 12,6,4,2, etc. This is all new to me but I've been trying to figure out the best way to implement this and have not found anything that I understand to this point.
Here is my code. I feel like this should be easy but it is super frustrating!
var rank = "";
var title = objArrayTwo[i].Title;
var summary ={};
summary = groupBy(objArrayTwo);
for (var prop in summary) {
if (summary.hasOwnProperty(prop)) {
if(title == `${prop}`){
rank = `${summary[prop]}`;
}
}
}
function groupBy(items){
var result= {};
var sum;
$.each(items, function(index, item) {
sum = result[item.RequestName] || 0;
result[item.RequestName] = sum + parseInt(item.Rank);
});
return result;
}
var obj = {TestOne: 12, TestTwo: 6, TestThree: 4, TestFour: 2};
// sort the array
var arr = Object.entries(obj);
arr.sort((a, b) => a[1] - b[1]);
// enumerate the array the index would be the rank
var arr_with_rank = arr.map((data, index) => [data[0], index+1]).reverse()
arr_with_rank.forEach(x => console.log(x[0] + ": " + x[1]));
ES5 solution
var resultDataObject = {
TestOne: 12,
TestTwo: 6,
TestThree: 4,
TestFour: 2
};
var descendingSort = function(a, b) {
return resultDataObject[b] - resultDataObject[a]
};
var sortedResultKeys = Object.keys(resultDataObject).sort(descendingSort);
var resultWithRank = sortedResultKeys.map(function(k, i) {
return {
title: k,
score: resultDataObject[k],
rank: i + 1
};
})
console.log(resultWithRank)
For example, if I have these arrays:
var name = ["Bob","Tom","Larry"];
var age = ["10", "20", "30"];
And I use name.sort() the order of the "name" array becomes:
var name = ["Bob","Larry","Tom"];
But, how can I sort the "name" array and have the "age" array keep the same order? Like this:
var name = ["Bob","Larry","Tom"];
var age = ["10", "30", "20"];
You can sort the existing arrays, or reorganize the data.
Method 1:
To use the existing arrays, you can combine, sort, and separate them:
(Assuming equal length arrays)
var names = ["Bob","Tom","Larry"];
var ages = ["10", "20", "30"];
//1) combine the arrays:
var list = [];
for (var j = 0; j < names.length; j++)
list.push({'name': names[j], 'age': ages[j]});
//2) sort:
list.sort(function(a, b) {
return ((a.name < b.name) ? -1 : ((a.name == b.name) ? 0 : 1));
//Sort could be modified to, for example, sort on the age
// if the name is the same. See Bonus section below
});
//3) separate them back out:
for (var k = 0; k < list.length; k++) {
names[k] = list[k].name;
ages[k] = list[k].age;
}
This has the advantage of not relying on string parsing techniques, and could be used on any number of arrays that need to be sorted together.
Method 2: Or you can reorganize the data a bit, and just sort a collection of objects:
var list = [
{name: "Bob", age: 10},
{name: "Tom", age: 20},
{name: "Larry", age: 30}
];
list.sort(function(a, b) {
return ((a.name < b.name) ? -1 : ((a.name == b.name) ? 0 : 1));
});
for (var i = 0; i<list.length; i++) {
alert(list[i].name + ", " + list[i].age);
}
For the comparisons,-1 means lower index, 0 means equal, and 1 means higher index. And it is worth noting that sort() actually changes the underlying array.
Also worth noting, method 2 is more efficient as you do not have to loop through the entire list twice in addition to the sort.
http://jsfiddle.net/ghBn7/38/
Bonus Here is a generic sort method that takes one or more property names.
function sort_by_property(list, property_name_list) {
list.sort((a, b) => {
for (var p = 0; p < property_name_list.length; p++) {
prop = property_name_list[p];
if (a[prop] < b[prop]) {
return -1;
} else if (a[prop] !== a[prop]) {
return 1;
}
}
return 0;
});
}
Usage:
var list = [
{name: "Bob", age: 10},
{name: "Tom", age: 20},
{name: "Larry", age: 30},
{name: "Larry", age: 25}
];
sort_by_property(list, ["name", "age"]);
for (var i = 0; i<list.length; i++) {
console.log(list[i].name + ", " + list[i].age);
}
Output:
Bob, 10
Larry, 25
Larry, 30
Tom, 20
You could get the indices of name array using Array.from(name.keys()) or [...name.keys()]. Sort the indices based on their value. Then use map to get the value for the corresponding indices in any number of related arrays
const indices = Array.from(name.keys())
indices.sort( (a,b) => name[a].localeCompare(name[b]) )
const sortedName = indices.map(i => name[i]),
const sortedAge = indices.map(i => age[i])
Here's a snippet:
const name = ["Bob","Tom","Larry"],
age = ["10", "20", "30"],
indices = Array.from(name.keys())
.sort( (a,b) => name[a].localeCompare(name[b]) ),
sortedName = indices.map(i => name[i]),
sortedAge = indices.map(i => age[i])
console.log(indices)
console.log(sortedName)
console.log(sortedAge)
This solution (my work) sorts multiple arrays, without transforming the data to an intermediary structure, and works on large arrays efficiently. It allows passing arrays as a list, or object, and supports a custom compareFunction.
Usage:
let people = ["john", "benny", "sally", "george"];
let peopleIds = [10, 20, 30, 40];
sortArrays([people, peopleIds]);
[["benny", "george", "john", "sally"], [20, 40, 10, 30]] // output
sortArrays({people, peopleIds});
{"people": ["benny", "george", "john", "sally"], "peopleIds": [20, 40, 10, 30]} // output
Algorithm:
Create a list of indexes of the main array (sortableArray)
Sort the indexes with a custom compareFunction that compares the values, looked up with the index
For each input array, map each index, in order, to its value
Implementation:
/**
* Sorts all arrays together with the first. Pass either a list of arrays, or a map. Any key is accepted.
* Array|Object arrays [sortableArray, ...otherArrays]; {sortableArray: [], secondaryArray: [], ...}
* Function comparator(?,?) -> int optional compareFunction, compatible with Array.sort(compareFunction)
*/
function sortArrays(arrays, comparator = (a, b) => (a < b) ? -1 : (a > b) ? 1 : 0) {
let arrayKeys = Object.keys(arrays);
let sortableArray = Object.values(arrays)[0];
let indexes = Object.keys(sortableArray);
let sortedIndexes = indexes.sort((a, b) => comparator(sortableArray[a], sortableArray[b]));
let sortByIndexes = (array, sortedIndexes) => sortedIndexes.map(sortedIndex => array[sortedIndex]);
if (Array.isArray(arrays)) {
return arrayKeys.map(arrayIndex => sortByIndexes(arrays[arrayIndex], sortedIndexes));
} else {
let sortedArrays = {};
arrayKeys.forEach((arrayKey) => {
sortedArrays[arrayKey] = sortByIndexes(arrays[arrayKey], sortedIndexes);
});
return sortedArrays;
}
}
See also https://gist.github.com/boukeversteegh/3219ffb912ac6ef7282b1f5ce7a379ad
If performance matters, there is sort-ids package for that purpose:
var sortIds = require('sort-ids')
var reorder = require('array-rearrange')
var name = ["Bob","Larry","Tom"];
var age = [30, 20, 10];
var ids = sortIds(age)
reorder(age, ids)
reorder(name, ids)
That is ~5 times faster than the comparator function.
It is very similar to jwatts1980's answer (Update 2).
Consider reading Sorting with map.
name.map(function (v, i) {
return {
value1 : v,
value2 : age[i]
};
}).sort(function (a, b) {
return ((a.value1 < b.value1) ? -1 : ((a.value1 == b.value1) ? 0 : 1));
}).forEach(function (v, i) {
name[i] = v.value1;
age[i] = v.value2;
});
You are trying to sort 2 independet arrays by only calling sort() on one of them.
One way of achieving this would be writing your own sorting methd which would take care of this, meaning when it swaps 2 elements in-place in the "original" array, it should swap 2 elements in-place in the "attribute" array.
Here is a pseudocode on how you might try it.
function mySort(originals, attributes) {
// Start of your sorting code here
swap(originals, i, j);
swap(attributes, i, j);
// Rest of your sorting code here
}
inspired from #jwatts1980's answer, and #Alexander's answer here I merged both answer's into a quick and dirty solution;
The main array is the one to be sorted, the rest just follows its indexes
NOTE: Not very efficient for very very large arrays
/* #sort argument is the array that has the values to sort
#followers argument is an array of arrays which are all same length of 'sort'
all will be sorted accordingly
example:
sortMutipleArrays(
[0, 6, 7, 8, 3, 4, 9],
[ ["zr", "sx", "sv", "et", "th", "fr", "nn"],
["zero", "six", "seven", "eight", "three", "four", "nine"]
]
);
// Will return
{
sorted: [0, 3, 4, 6, 7, 8, 9],
followed: [
["zr", th, "fr", "sx", "sv", "et", "nn"],
["zero", "three", "four", "six", "seven", "eight", "nine"]
]
}
*/
You probably want to change the method signature/return structure, but that should be easy though. I did it this way because I needed it
var sortMultipleArrays = function (sort, followers) {
var index = this.getSortedIndex(sort)
, followed = [];
followers.unshift(sort);
followers.forEach(function(arr){
var _arr = [];
for(var i = 0; i < arr.length; i++)
_arr[i] = arr[index[i]];
followed.push(_arr);
});
var result = {sorted: followed[0]};
followed.shift();
result.followed = followed;
return result;
};
var getSortedIndex = function (arr) {
var index = [];
for (var i = 0; i < arr.length; i++) {
index.push(i);
}
index = index.sort((function(arr){
/* this will sort ints in descending order, change it based on your needs */
return function (a, b) {return ((arr[a] > arr[b]) ? -1 : ((arr[a] < arr[b]) ? 1 : 0));
};
})(arr));
return index;
};
I was looking for something more generic and functional than the current answers.
Here's what I came up with: an es6 implementation (with no mutations!) that lets you sort as many arrays as you want given a "source" array
/**
* Given multiple arrays of the same length, sort one (the "source" array), and
* sort all other arrays to reorder the same way the source array does.
*
* Usage:
*
* sortMultipleArrays( objectWithArrays, sortFunctionToApplyToSource )
*
* sortMultipleArrays(
* {
* source: [...],
* other1: [...],
* other2: [...]
* },
* (a, b) => { return a - b })
* )
*
* Returns:
* {
* source: [..sorted source array]
* other1: [...other1 sorted in same order as source],
* other2: [...other2 sorted in same order as source]
* }
*/
export function sortMultipleArrays( namedArrays, sortFn ) {
const { source } = namedArrays;
if( !source ) {
throw new Error('You must pass in an object containing a key named "source" pointing to an array');
}
const arrayNames = Object.keys( namedArrays );
// First build an array combining all arrays into one, eg
// [{ source: 'source1', other: 'other1' }, { source: 'source2', other: 'other2' } ...]
return source.map(( value, index ) =>
arrayNames.reduce((memo, name) => ({
...memo,
[ name ]: namedArrays[ name ][ index ]
}), {})
)
// Then have user defined sort function sort the single array, but only
// pass in the source value
.sort(( a, b ) => sortFn( a.source, b.source ))
// Then turn the source array back into an object with the values being the
// sorted arrays, eg
// { source: [ 'source1', 'source2' ], other: [ 'other1', 'other2' ] ... }
.reduce(( memo, group ) =>
arrayNames.reduce((ongoingMemo, arrayName) => ({
...ongoingMemo,
[ arrayName ]: [
...( ongoingMemo[ arrayName ] || [] ),
group[ arrayName ]
]
}), memo), {});
}
You could append the original index of each member to the value, sort the array, then remove the index and use it to re-order the other array. It will only work where the contents are strings or can be converted to and from strings successfuly.
Another solution is keep a copy of the original array, then after sorting, find where each member is now and adjust the other array appropriately.
I was having the same issue and came up with this incredibly simple solution. First combine the associated ellements into strings in a seperate array then use parseInt in your sort comparison function like this:
<html>
<body>
<div id="outPut"></div>
<script>
var theNums = [13,12,14];
var theStrs = ["a","b","c"];
var theCombine = [];
for (var x in theNums)
{
theCombine[x] = theNums[x] + "," + theStrs;
}
var theSorted = theAr.sort(function(a,b)
{
var c = parseInt(a,10);
var d = parseInt(b,10);
return c-d;
});
document.getElementById("outPut").innerHTML = theS;
</script>
</body>
</html>
How about:
var names = ["Bob","Tom","Larry"];
var ages = ["10", "20", "30"];
var n = names.slice(0).sort()
var a = [];
for (x in n)
{
i = names.indexOf(n[x]);
a.push(ages[i]);
names[i] = null;
}
names = n
ages = a
Simplest explantion is the best, merge the arrays, and then extract after sorting:
create an array
name_age=["bob#10","Tom#20","Larry#30"];
sort the array as before, then extract the name and the age, you can use # to reconise where
name ends and age begins. Maybe not a method for the purist, but I have the same issue and this my approach.