Sort two arrays the same way - javascript

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.

Related

How to extract a sub-array from two arrays with high performances?

I have two arrays of JSON objects :
one contains about 60.000 elements, which represents my reference dataset. Each JSON inside owns a key and some other attributes. Please note that the key might be not uniq in the array.
another one contains a various number of elements (at least a few thousands). Each JSON inside owns a key (that is also defined in the first array), and some other attributes.
e.g. :
let refarray = [{key : 1, attr1 : 'aze', ...}, {key : 1, attr1 : 'zer', ...},{key : 2, attr1 : 'ert'},...]
let otherarray = [{key : 1, attr2 : 'wxc', ...}, {key : 3, attr2 : 'xcv'},...]
I simply need to extract from refarray all elements whose key exists in otherarray.
For the moment I'm using loadash as following :
let newarray = _.filter(refarray , function(d) { return _.findIndex(otherarray , function(s) { return s.key=== d.key;}) >= 0});
But it takes between 3 and 15 seconds, which is far too long. Any quickest solution is welcome. Thanks.
You may try caching the keys of otherarray and then filter refarray. I tried a small sample (although I tried on node and not browser) and it was taking a little over 100 ms:
let refarray = []
let otherarray = []
for(let i of Array(60 * 1000).keys())
refarray.push({ key: 1 + (i % 1200) })
for(let i of Array(1000).keys())
otherarray.push({ key: i + 1 })
console.time('cache')
let cache = _.uniq(_.map(otherarray, n => n.key))
const inCache = n => cache.indexOf(n.key) !== -1
let newArray = _.filter(refarray, inCache)
console.timeEnd('cache')
console.log(refarray.length, otherarray.length, newArray.length);
Depending on the amount of duplicate keys, solution by Emil S. Jørgensen might not be optimal enough. I would go with iterating over distinct values of 1st array:
d2 = Date.now();
var distinct = [];
refarray.forEach(function(item) {
if (distinct.indexOf(item.key) < 0) {
distinct.push(item.key);
}
});
console.log('Results:',otherarray.filter(function(item) {
return distinct.indexOf(item.key) > -1;
}));
console.log('Milliseconds to filter:', Date.now() - d2);
Array.prototype.filter with Array.prototype.some should be the fastest approach.
//Default ref
var refarray = [{
key: 1,
attr1: 'aze'
}, {
key: 2,
attr1: 'zer'
}];
//Default other
var otherarray = [{
key: 1,
attr2: 'wxc'
}, {
key: 3,
attr2: 'xcv'
}];
//Padding ref
while (refarray.length < 10 * 1000) {
refarray.push({
key: 5,
attr1: 'aze'
})
}
//Padding other
while (otherarray.length < 60 * 1000) {
otherarray.push({
key: 6,
attr2: 'aze'
})
}
console.log('Size of refarray:', refarray.length);
console.log('Size of otherarray:', otherarray.length);
var d = Date.now();
console.log('Results:',refarray.filter(function(a) {
return otherarray.some(function(b) {
return b.key == a.key
})
}));
console.log('Milliseconds to filter:', Date.now() - d);

why is .sort return 0 reordering my results? [duplicate]

I'm looking to sort an array of about 200-300 objects, sorting on a specific key and a given order (asc/desc). The order of results must be consistent and stable.
What would be the best algorithm to use, and could you provide an example of it's implementation in javascript?
Thanks!
It is possible to get a stable sorting from a non-stable sort function.
Before sorting you get the position of all the elements.
In your sort condition, if both elements are equal, then you sort by the position.
Tada! You've got a stable sort.
I've written an article about it on my blog if you want to know more about this technique and how to implement it: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html
Since you are looking for something stable, the merge sort should do.
http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/
The code can be found at the above website:
function mergeSort(arr)
{
if (arr.length < 2)
return arr;
var middle = parseInt(arr.length / 2);
var left = arr.slice(0, middle);
var right = arr.slice(middle, arr.length);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right)
{
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
EDIT:
According to this post, it looks like Array.Sort in some implementations uses a merge sort.
Somewhat shorter version of the same thing using ES2017 features like arrow functions and destructuring:
Function
var stableSort = (arr, compare) => arr
.map((item, index) => ({item, index}))
.sort((a, b) => compare(a.item, b.item) || a.index - b.index)
.map(({item}) => item)
It accepts input array and compare function:
stableSort([5,6,3,2,1], (a, b) => a - b)
It also returns new array instead of making in-place sort like the built-in Array.sort() function.
Test
If we take the following input array, initially sorted by weight:
// sorted by weight
var input = [
{ height: 100, weight: 80 },
{ height: 90, weight: 90 },
{ height: 70, weight: 95 },
{ height: 100, weight: 100 },
{ height: 80, weight: 110 },
{ height: 110, weight: 115 },
{ height: 100, weight: 120 },
{ height: 70, weight: 125 },
{ height: 70, weight: 130 },
{ height: 100, weight: 135 },
{ height: 75, weight: 140 },
{ height: 70, weight: 140 }
]
Then sort it by height using stableSort:
stableSort(input, (a, b) => a.height - b.height)
Results in:
// Items with the same height are still sorted by weight
// which means they preserved their relative order.
var stable = [
{ height: 70, weight: 95 },
{ height: 70, weight: 125 },
{ height: 70, weight: 130 },
{ height: 70, weight: 140 },
{ height: 75, weight: 140 },
{ height: 80, weight: 110 },
{ height: 90, weight: 90 },
{ height: 100, weight: 80 },
{ height: 100, weight: 100 },
{ height: 100, weight: 120 },
{ height: 100, weight: 135 },
{ height: 110, weight: 115 }
]
However sorting the same input array using the built-in Array.sort() (in Chrome/NodeJS):
input.sort((a, b) => a.height - b.height)
Returns:
var unstable = [
{ height: 70, weight: 140 },
{ height: 70, weight: 95 },
{ height: 70, weight: 125 },
{ height: 70, weight: 130 },
{ height: 75, weight: 140 },
{ height: 80, weight: 110 },
{ height: 90, weight: 90 },
{ height: 100, weight: 100 },
{ height: 100, weight: 80 },
{ height: 100, weight: 135 },
{ height: 100, weight: 120 },
{ height: 110, weight: 115 }
]
Resources
Wikipedia
MDN
JSFiddle
Update
Array.prototype.sort is now stable in V8 v7.0 / Chrome 70!
Previously, V8 used an unstable QuickSort for arrays with more than 10 elements. Now, we use the stable TimSort algorithm.
source
I know that this question has been answered for some time, but I happen to have a good stable merge sort implementation for Array and jQuery in my clipboard, so I'll share it in the hopes that some future searchers might find it useful.
It allows you to specify your own comparison function just like the normal Array.sort implementation.
Implementation
// Add stable merge sort to Array and jQuery prototypes
// Note: We wrap it in a closure so it doesn't pollute the global
// namespace, but we don't put it in $(document).ready, since it's
// not dependent on the DOM
(function() {
// expose to Array and jQuery
Array.prototype.mergeSort = jQuery.fn.mergeSort = mergeSort;
function mergeSort(compare) {
var length = this.length,
middle = Math.floor(length / 2);
if (!compare) {
compare = function(left, right) {
if (left < right)
return -1;
if (left == right)
return 0;
else
return 1;
};
}
if (length < 2)
return this;
return merge(
this.slice(0, middle).mergeSort(compare),
this.slice(middle, length).mergeSort(compare),
compare
);
}
function merge(left, right, compare) {
var result = [];
while (left.length > 0 || right.length > 0) {
if (left.length > 0 && right.length > 0) {
if (compare(left[0], right[0]) <= 0) {
result.push(left[0]);
left = left.slice(1);
}
else {
result.push(right[0]);
right = right.slice(1);
}
}
else if (left.length > 0) {
result.push(left[0]);
left = left.slice(1);
}
else if (right.length > 0) {
result.push(right[0]);
right = right.slice(1);
}
}
return result;
}
})();
Example Usage
var sorted = [
'Finger',
'Sandwich',
'sandwich',
'5 pork rinds',
'a guy named Steve',
'some noodles',
'mops and brooms',
'Potato Chip Brand® chips'
].mergeSort(function(left, right) {
lval = left.toLowerCase();
rval = right.toLowerCase();
console.log(lval, rval);
if (lval < rval)
return -1;
else if (lval == rval)
return 0;
else
return 1;
});
sorted == ["5 pork rinds", "a guy named Steve", "Finger", "mops and brooms", "Potato Chip Brand® chips", "Sandwich", "sandwich", "some noodles"];
You can use the following function to perform a stable sort regardless of the native implementation, based on the assertion made in this answer.
Do note that as of ECMAScript 2019, the specification requires that the builtin sort() method perform a stable sort. With that in mind, an explicit stable sort function like the one below is still relevant if you are required to support older browsers that are not specification compliant.
// ECMAScript 5 implementation
function stableSort(array, compareFunction) {
'use strict';
var length = array.length;
var indices = new Uint32Array(length);
var i;
var slice;
// reference values by indices
for (i = 0; i < length; ++i) {
indices[i] = i;
}
// sort with fallback based on indices
indices.sort(function stableCompareFunction(compareFunction, a, b) {
var order = Number(compareFunction(this[a], this[b]));
return order || a - b;
}.bind(array, compareFunction));
slice = array.slice();
// re-order original array to stable sorted values
for (i = 0; i < length; ++i) {
array[i] = slice[indices[i]];
}
return array;
}
// usage
const array = Array(500000).fill().map(() => Number(Math.random().toFixed(4)));
const alwaysEqual = () => 0;
const isUnmoved = (value, index) => value === array[index];
// not guaranteed to be stable before ES2019
console.log(
'sort() stable?',
array.slice().sort(alwaysEqual).every(isUnmoved)
);
// guaranteed to be stable
console.log(
'stableSort() stable?',
stableSort(array.slice(), alwaysEqual).every(isUnmoved)
);
// performance using realistic scenario with unsorted big data
function time(arraySlice, algorithm, compare) {
var start;
var stop;
start = performance.now();
algorithm(arraySlice, compare);
stop = performance.now();
return stop - start;
}
const ascending = (a, b) => a - b;
const msSort = time(array.slice(), (array, compare) => array.sort(compare), ascending);
const msStableSort = time(array.slice(), (array, compare) => stableSort(array, compare), ascending);
console.log('sort()', msSort.toFixed(3), 'ms');
console.log('stableSort()', msStableSort.toFixed(3), 'ms');
console.log('sort() / stableSort()', (100 * msSort / msStableSort).toFixed(3) + '%');
Running the performance tests implemented above, stableSort() appears to run at about 72% of the speed of sort() on version 88 of Google Chrome and Microsoft Edge.
Using .bind() on the inline function within stableSort() used to boost relative performance significantly by avoiding unneeded scoped references on each call.
In practice, this no longer makes a difference since modern engines automatically perform this optimization now, but it is left in the implementation anyway in order to continue improving performance in older browsers which don't ship with this optimization.
The following sorts the supplied array, by applying the supplied compare function, returning the original index comparison when the compare function returns 0:
function stableSort(arr, compare) {
var original = arr.slice(0);
arr.sort(function(a, b){
var result = compare(a, b);
return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
});
return arr;
}
The example below sorts an array of names by surname, retaining the order of equal surnames:
var names = [
{ surname: "Williams", firstname: "Mary" },
{ surname: "Doe", firstname: "Mary" },
{ surname: "Johnson", firstname: "Alan" },
{ surname: "Doe", firstname: "John" },
{ surname: "White", firstname: "John" },
{ surname: "Doe", firstname: "Sam" }
]
function stableSort(arr, compare) {
var original = arr.slice(0);
arr.sort(function(a, b){
var result = compare(a, b);
return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
});
return arr;
}
stableSort(names, function(a, b) {
return a.surname > b.surname ? 1 : a.surname < b.surname ? -1 : 0;
})
names.forEach(function(name) {
console.log(name.surname + ', ' + name.firstname);
});
Here's a stable implementation. It works by using the native sort, but in cases where elements compare as equal, you break ties using the original index position.
function stableSort(arr, cmpFunc) {
//wrap the arr elements in wrapper objects, so we can associate them with their origional starting index position
var arrOfWrapper = arr.map(function(elem, idx){
return {elem: elem, idx: idx};
});
//sort the wrappers, breaking sorting ties by using their elements orig index position
arrOfWrapper.sort(function(wrapperA, wrapperB){
var cmpDiff = cmpFunc(wrapperA.elem, wrapperB.elem);
return cmpDiff === 0
? wrapperA.idx - wrapperB.idx
: cmpDiff;
});
//unwrap and return the elements
return arrOfWrapper.map(function(wrapper){
return wrapper.elem;
});
}
a non-thorough test
var res = stableSort([{a:1, b:4}, {a:1, b:5}], function(a, b){
return a.a - b.a;
});
console.log(res);
another answer alluded to this, but didn't post teh codez.
but, its not fast according to my benchmark. I modified a merge sort impl to accept a custom comparator function, and it was much faster.
You can also use Timsort. This is a really complicated algorithm (400+ lines, hence no source code here), so see Wikipedia's description or use one of the existing JavaScript implementations:
GPL 3 implementation. Packaged as Array.prototype.timsort. Appears to be an exact rewrite of Java code.
Public domain implementation Meant as a tutorial, the sample code only shows its use with integers.
Timsort is a highly optimized hybrid of mergesort and shuffle sort and is the default sorting algorithm in Python and in Java (1.7+). It is a complicated algorithm, since it uses different algorithms for many special cases. But as a result it's extremely fast under a wide variety of circumstances.
A simple one mergeSort from http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/
var a = [34, 203, 3, 746, 200, 984, 198, 764, 9];
function mergeSort(arr)
{
if (arr.length < 2)
return arr;
var middle = parseInt(arr.length / 2);
var left = arr.slice(0, middle);
var right = arr.slice(middle, arr.length);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right)
{
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
console.log(mergeSort(a));
I have to sort multidimensional arrays by an arbitrary column, and then by another. I use this function to sort:
function sortMDArrayByColumn(ary, sortColumn){
//Adds a sequential number to each row of the array
//This is the part that adds stability to the sort
for(var x=0; x<ary.length; x++){ary[x].index = x;}
ary.sort(function(a,b){
if(a[sortColumn]>b[sortColumn]){return 1;}
if(a[sortColumn]<b[sortColumn]){return -1;}
if(a.index>b.index){
return 1;
}
return -1;
});
}
Notice that ary.sort never returns zero, which is where some implementations of the "sort" function make decisions that might not be right.
This is pretty darn fast, too.
Here's how you could extend JS default Array object with a prototype method utilizing MERGE SORT. This method allows for sorting on a specific key (first parameter) and a given order ('asc'/'desc' as second parameter)
Array.prototype.mergeSort = function(sortKey, direction){
var unsortedArray = this;
if(unsortedArray.length < 2) return unsortedArray;
var middle = Math.floor(unsortedArray.length/2);
var leftSubArray = unsortedArray.slice(0,middle).mergeSort(sortKey, direction);
var rightSubArray = unsortedArray.slice(middle).mergeSort(sortKey, direction);
var sortedArray = merge(leftSubArray, rightSubArray);
return sortedArray;
function merge(left, right) {
var combined = [];
while(left.length>0 && right.length>0){
var leftValue = (sortKey ? left[0][sortKey] : left[0]);
var rightValue = (sortKey ? right[0][sortKey] : right[0]);
combined.push((direction === 'desc' ? leftValue > rightValue : leftValue < rightValue) ? left.shift() : right.shift())
}
return combined.concat(left.length ? left : right)
}
}
You can test this out yourself by dropping the above snippet into your browser console, then trying:
var x = [2,76,23,545,67,-9,12];
x.mergeSort(); //[-9, 2, 12, 23, 67, 76, 545]
x.mergeSort(undefined, 'desc'); //[545, 76, 67, 23, 12, 2, -9]
Or order based on a specific field in an array of objects:
var y = [
{startTime: 100, value: 'cat'},
{startTime: 5, value: 'dog'},
{startTime: 23, value: 'fish'},
{startTime: 288, value: 'pikachu'}
]
y.mergeSort('startTime');
y.mergeSort('startTime', 'desc');
So I needed a stable sort for my React+Redux app, and Vjeux's answer here helped me. However, my (generic) solution seems different than the others I see here so far, so I'm sharing it in case anyone else has a matching use-case:
I really just want to have something similar to the sort() API, where I can pass a comparator function.
Sometimes I can sort in-place, and sometimes my data is immutable (because Redux) and I need a sorted copy instead. So I need a stable sorting function for each use-case.
ES2015.
My solution is to create a typed array of indices, then use a comparison function to sort these indices based on the to-be-sorted array. Then we can use the sorted indices to either sort the original array or create a sorted copy in a single pass. If that's confusing, think of it this way: where you would normally pass a comparison function like:
(a, b) => {
/* some way to compare a and b, returning -1, 0, or 1 */
};
You now instead use:
(i, j) => {
let a = arrayToBeSorted[i], b = arrayToBeSorted[j];
/* some way to compare a and b, returning -1 or 1 */
return i - j; // fallback when a == b
}
Speed is good; it is basically the built-in sorting algorithm is, plus two linear passes in the end, and one extra layer of pointer indirection overhead.
Happy to receive feedback on this approach. Here is my full implementation of it it:
/**
* - `array`: array to be sorted
* - `comparator`: closure that expects indices `i` and `j`, and then
* compares `array[i]` to `array[j]` in some way. To force stability,
* end with `i - j` as the last "comparison".
*
* Example:
* ```
* let array = [{n: 1, s: "b"}, {n: 1, s: "a"}, {n:0, s: "a"}];
* const comparator = (i, j) => {
* const ni = array[i].n, nj = array[j].n;
* return ni < nj ? -1 :
* ni > nj ? 1 :
* i - j;
* };
* stableSortInPlace(array, comparator);
* // ==> [{n:0, s: "a"}, {n:1, s: "b"}, {n:1, s: "a"}]
* ```
*/
function stableSortInPlace(array, comparator) {
return sortFromIndices(array, findIndices(array, comparator));
}
function stableSortedCopy(array, comparator){
let indices = findIndices(array, comparator);
let sortedArray = [];
for (let i = 0; i < array.length; i++){
sortedArray.push(array[indices[i]]);
}
return sortedArray;
}
function findIndices(array, comparator){
// Assumes we don't have to worry about sorting more than
// 4 billion elements; if you know the upper bounds of your
// input you could replace it with a smaller typed array
let indices = new Uint32Array(array.length);
for (let i = 0; i < indices.length; i++) {
indices[i] = i;
}
// after sorting, `indices[i]` gives the index from where
// `array[i]` should take the value from, so to sort
// move the value at at `array[indices[i]]` to `array[i]`
return indices.sort(comparator);
}
// If I'm not mistaken this is O(2n) - each value is moved
// only once (not counting the vacancy temporaries), and
// we also walk through the whole array once more to check
// for each cycle.
function sortFromIndices(array, indices) {
// there might be multiple cycles, so we must
// walk through the whole array to check.
for (let k = 0; k < array.length; k++) {
// advance until we find a value in
// the "wrong" position
if (k !== indices[k]) {
// create vacancy to use "half-swaps" trick,
// props to Andrei Alexandrescu
let v0 = array[k];
let i = k;
let j = indices[k];
while (j !== k) {
// half-swap next value
array[i] = array[j];
// array[i] now contains the value it should have,
// so we update indices[i] to reflect this
indices[i] = i;
// go to next index
i = j;
j = indices[j];
}
// put original array[k] back in
// and update indices
array[i] = v0;
indices[i] = i;
}
}
return array;
}
I know this has been plenty answered. I just wanted to go ahead an post a quick TS implementation for anyone that landed here looking for that.
export function stableSort<T>( array: T[], compareFn: ( a: T, b: T ) => number ): T[] {
const indices = array.map( ( x: T, i: number ) => ( { element: x, index: i } ) );
return indices.sort( ( a, b ) => {
const order = compareFn( a.element, b.element );
return order === 0 ? a.index - b.index : order;
} ).map( x => x.element );
}
The method does no longer run in-place, as the native sort does. I also want to point out that it is not the most efficient. It adds two loops of the order O(n). though sort itself is most likely O(n log(n)) so it's less than that.
Some of the solutions mentioned are more performant, thought this might be less code, also using internal Array.prototype.sort.
(For a Javascript solution, just remove all the types)
According to the v8 dev blog and caniuse.com Array.sort is already stable as required by the spec in modern browsers, so you don't need to roll your own solution.
The only exception I can see is Edge, which should soon move over to chromium and support it as well.
function sort(data){
var result=[];
var array = data;
const array2=data;
const len=array2.length;
for(var i=0;i<=len-1;i++){
var min = Math.min.apply(Math,array)
result.push(min);
var index=array.indexOf(min)
array.splice(index,1);
}
return result;
}
sort([9,8,5,7,9,3,9,243,4,5,6,3,4,2,4,7,4,9,55,66,33,66]);
Counting Sort is faster than merge sort (it performs in O(n) time) and it is intended for use on integers.
Math.counting_sort = function (m) {
var i
var j
var k
var step
var start
var Output
var hash
k = m.length
Output = new Array ()
hash = new Array ()
// start at lowest possible value of m
start = 0
step = 1
// hash all values
i = 0
while ( i < k ) {
var _m = m[i]
hash [_m] = _m
i = i + 1
}
i = 0
j = start
// find all elements within x
while ( i < k ) {
while ( j != hash[j] ) {
j = j + step
}
Output [i] = j
i = i + 1
j = j + step
}
return Output
}
Example:
var uArray = new Array ()<br/>
var sArray = new Array ()<br/><br/>
uArray = [ 10,1,9,2,8,3,7,4,6,5 ]<br/>
sArray = Math.counting_sort ( uArray ) // returns a sorted array

JavaScript move an item of an array to the front

I want to check if an array contains "role". If it does, I want to move the "role" to the front of the array.
var data= ["email","role","type","name"];
if ("role" in data) data.remove(data.indexOf("role")); data.unshift("role")
data;
Here, I got the result:
["role", "email", "role", "type", "name"]
How can I fix this?
You can sort the array and specify that the value "role" comes before all other values, and that all other values are equal:
var first = "role";
data.sort(function(x,y){ return x == first ? -1 : y == first ? 1 : 0; });
Demo: http://jsfiddle.net/Guffa/7ST24/
The cleanest solution in ES6 in my opinion:
let data = ["email","role","type","name"];
data = data.filter(item => item !== "role");
data.unshift("role");
let data = [0, 1, 2, 3, 4, 5];
let index = 3;
data.unshift(data.splice(index, 1)[0]);
// data = [3, 0, 1, 2, 4, 5]
My first thought would be:
var data= ["email","role","type","name"];
// if it's not there, or is already the first element (of index 0)
// then there's no point going further:
if (data.indexOf('role') > 0) {
// find the current index of 'role':
var index = data.indexOf('role');
// using splice to remove elements from the array, starting at
// the identified index, and affecting 1 element(s):
data.splice(index,1);
// putting the 'role' string back in the array:
data.unshift('role');
}
console.log(data);
To revise, and tidy up a little:
if (data.indexOf('role') > 0) {
data.splice(data.indexOf('role'), 1);
data.unshift('role');
}
References:
Array.indexOf().
Array.prototype.splice().
Array.unshift().
Here is an immutable solution if needed :
const newData = [
data.find(item => item === 'role'),
...data.filter(item => item !== 'role'),
],
If you don't want to alter the existing array, you can use ES6 destructuring with the filter method to create a new copy while maintaining the order of the other items.
const data = ["email", "role", "type", "name"];
const newData = ['role', ...data.filter(item => item !== 'role')];
If you have an array of objects you could shift the start-index with splice and push. Splice replaces the original array with the part of the array starting from the desired index and returns the part it removes (the stuff before the index) which you push.
let friends = [{
id: 1,
name: "Sam",
},
{
id: 2,
name: "Steven",
},
{
id: 3,
name: "Tom",
},
{
id: 4,
name: "Nora",
},
{
id: 5,
name: "Jessy",
}
];
const tomsIndex = friends.findIndex(friend => friend.name == 'Tom');
friends.push(...friends.splice(0, tomsIndex));
console.log(friends);
To check whether an item exists in an array you should to use .includes() instead of in (as already noted here, in is for properties in objects).
This function does what you are looking for:
(removes the item from the position it is in and reads
in front)
data = ["email","role","type","name"];
moveToFirst("role", data);
function moveToFirst( stringToMove, arrayIn ){
if ( arrayIn.includes(stringToMove) ){
let currentIndex = arrayIn.indexOf(stringToMove);
arrayIn.splice(currentIndex, 1);
arrayIn.unshift(stringToMove);
}
}
console.log(data);
Similar to #Tandroid's answer but a more general solution:
const putItemsFirst = ({ findFunction, array }) => [
...array.filter(findFunction),
...array.filter(item => !findFunction(item)),
];
Can be used like this
putItemsFirst({
array: ["email","role","type","name"],
findFunction: item => item === 'role',
})
Something similar to this is what I ended up using,
I would go with this ES6 solution. It doesn't mutate the original array(considering it's not nested), doesn't traverse through the array(filter) and you're not just limited to 0th index for shifting the array item.
const moveArrayItem = (array, fromIndex, toIndex) => {
const arr = [...array];
arr.splice(toIndex, 0, ...arr.splice(fromIndex, 1));
return arr;
}
const arr = ["a", "b", "c", "d", "e", "f", "g"];
console.log(moveArrayItem(arr, 4, 0))
// [ 'e', 'a', 'b', 'c', 'd', 'f', 'g' ]
The most readable way in my opinion.
array.sort((a, b) => (a === value && -1) || (b === value && 1) || 0)
var data= ["email","role","type","name"];
data.splice(data.indexOf("role"), 1);
data.unshift('role');
You could take the delta of the check with the wanted value at top.
var data = ["email", "role", "type", "name"];
data.sort((a, b) => (b === 'role') - (a === 'role'));
console.log(data);
A reusable ES6/Typescript solution:
const moveToStart = <T>(array: T[], predicate: (item: T) => boolean): T[] => {
return array.sort((a, b) => {
if (predicate(a)) return -1;
if (predicate(b)) return 1;
return 0;
});
};
const data = ["email", "role", "type", "name"];
const result = moveToStart(data, (item) => item === "role"))
the in operator is about properties, not about items in arrays. See How do I check if an array includes an object in JavaScript? for what to use else.
You're missing braces around the two (!) statements in your if-block
I'm not sure whether that .remove() function you're using does take an index of an item.
Using lodash _.sortBy. If the item is role, it will be sorted first, otherwise second. This works fine too if there is no role
var data = ["email", "role", "type", "name"];
var sorted = _.sortBy(data, function(item) {
return item === 'role' ? 0 : 1;
});
console.log(sorted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Just wanted to drop this on here since according to other comments Guffa's answer seems to be gaining traction, the final tertiary - which was one of the negative comments on that answer is unnecessary. Also using arrow functions makes it seem much cleaner.
Also, it is easily expandable to handling Arrays of objects.
const first = "role";
data.sort((x, y) => first === x ? -1 : first === y)
I believe this should also handle the worry of the rest of the array being affected. When the sort function returns a number less than 0 (first === x), the element will move toward the start of the Array, when it returns 0 (first !== y), there will be no movement, and when a number greater than 0 (first === y), x will move toward the end of the Array, all in relation to x and y. Therefore, when neither x or y are equivalent to the desired first element (or it's identifier in the case of sorting objects), there will be no movement of the two in relation to each other.
For an object:
const unsorted = [{'id': 'test'}, {'id': 'something'}, {'id': 'else'}];
const first = 'something';
const sorted = unsorted.sort((x,y) => x['id'] === first ? -1 : y['id'] === first);
My solution is a bit different as it mutates original array instead of creating a new one.
It will move given item to start of the array and move item that was previously at start in the place of requested item.
function moveElementToStart<T>(items: T[], item: T) {
const itemIndex = items.indexOf(item);
// Item is not found or it is already on start
if (itemIndex === -1 || itemIndex === 0) return;
// Get item that is currently at start
const currentItemAtStart = items[0];
// Swap this item position with item we want to put on start
items[0] = item;
items[itemIndex] = currentItemAtStart;
}
Generalized one-liners:
const data = ["a", "b", "c", "d", "e", "f"];
const [from, take] = [3, 2];
data.unshift(...data.splice(from, take));
// alternatively
data = [...data.splice(from, take), ...data];
// ["d", "e", "a", "b", "c", "f"]
const moveToFront = (arr, queryStr) =>
arr.reduce((acc, curr) => {
if (queryStr === curr) {
return [curr, ...acc];
}
return [...acc, curr];
}, []);
const data = ['email', 'role', 'type', 'name'];
console.log(moveToFront(data, 'role'))
const moveTargetToBeginningOfArray = (arr, target) => {
// loop through array
for (let i = 0; i < arr.length; i++){
// if current indexed element is the target
if(arr[i] === target){
// remove that target element
arr.splice(i, 1)
// then add a target element to the beginning of the array
arr.unshift(target)
}
}
return arr;
};
// quick sanity check, before and after both are correct
const arrayOfStrings = ["email", "role", "type", "name", "role", "role"];
console.log('before:', arrayOfStrings)
console.log('after:', moveTargetToBeginningOfArray(arrayOfStrings, "role"))
// this would also work for numbers
var arrayOfNumbers = [2,4,0,3,0,1,0]
console.log('before:', arrayOfNumbers)
console.log('after:', moveTargetToBeginningOfArray(arrayOfNumbers, 0))
function unshiftFrom(arr, index) {
if (index > -1 && index < arr.length) { // validate index
var [itemToMove] = arr.splice(index, 1)
arr.unshift(itemToMove)
}
return arr // optional
}
//we can do this from scratch
let tempList=["person1","person2","person3"];
let result=[];
//suppose i need to move "person2" to first place
let movableValue=null;
let query="person2"; //here you could use any type of query based on your problem
tempList.map((e)=>{
if(e!==query){
result.push(e);
}else if(e===query){
movableValue=e;
}
})
if(movableValue!==null){
result.unshift(movableValue);
}
console.log(result)
)
var i = -1;
while (i < data.length) {
if (data[i] === "role") {
data.splice(i, 1);
break;
}
i++;
}
data.unshift("role");
indexOf only has limited browser support, not being recognized by IE7-8. So I wouldn't use it if I were you, even at the expense of a few lines' worth of code conciseness. You also want to put a semicolon at the end of the "unshift" statement. splice()'s first argument specifies the index to start removing elements, and the second argument specifies the number of arguments to remove.
data.unshift(data.splice(data.indexOf('role'), 1)[0])
data.indexOf('role') will find the index of 'role' in the array and then the original array is spliced to remove the 'role' element, which is added to the beginning of the array using unshift
var data= ["email","role","type","name"];
if ("role" in data) data.splice(data.indexOf("role"),1); data.unshift("role");
data;

Remove duplicate objects from an array using javascript

I am trying to figure out an efficient way to remove objects that are duplicates from an array and looking for the most efficient answer. I looked around the internet everything seems to be using primitive data... or not scalable for large arrays. This is my current implementation which is can be improved and want to try to avoid labels.
Test.prototype.unique = function (arr, artist, title, cb) {
console.log(arr.length);
var n, y, x, i, r;
r = [];
o: for (i = 0, n = arr.length; i < n; i++) {
for (x = 0, y = r.length; x < y; x++) {
if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
continue o;
}
}
r.push(arr[i]);
}
cb(r);
};
and the array looks something like this:
[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]
Order does not matter, but if sorting makes it more efficient then I am up for the challenge...
and for people who do not know o is a label and it is just saying jump back to the loop instead of pushing to the new array.
Pure javascript please no libs.
ANSWERS SO FAR:
The Performance Test for the answers below:
http://jsperf.com/remove-duplicates-for-loops
I see, the problem there is that the complexity is squared. There is one trick to do it, it's simply by using "Associative arrays".
You can get the array, loop over it, and add the value of the array as a key to the associative array. Since it doesn't allow duplicated keys, you will automatically get rid of the duplicates.
Since you are looking for title and artist when comparing, you can actually try to use something like:
var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
var item = arr[i];
arrResult[ item.title + " - " + item.artist ] = item;
}
Then you just loop the arrResult again, and recreate the array.
var i = 0;
var nonDuplicatedArray = [];
for(var item in arrResult) {
nonDuplicatedArray[i++] = arrResult[item];
}
Updated to include Paul's comment. Thanks!
Here is a solution that works for me.
Helper functions:
// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
arr.sort(
function compare(a,b) {
if (a[field] < b[field])
return -1;
if (a[field] > b[field])
return 1;
return 0;
}
);
}
// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
var u = [];
arr.reduce(function (a, b) {
if (a[field] !== b[field]) u.push(b);
return b;
}, []);
return u;
}
and then simply call:
sortObjArray(dishes, "name");
dishes = removeDuplicatesFromObjArray(dishes, "name");
Basic sort-then-unique implementation, fiddle HERE:
function unique(arr) {
var comparer = function compareObject(a, b) {
if (a.title == b.title) {
if (a.artist < b.artist) {
return -1;
} else if (a.artist > b.artist) {
return 1;
} else {
return 0;
}
} else {
if (a.title < b.title) {
return -1;
} else {
return 1;
}
}
}
arr.sort(comparer);
console.log("Sorted: " + JSON.stringify(arr));
for (var i = 0; i < arr.length - 1; ++i) {
if (comparer(arr[i], arr[i+1]) === 0) {
arr.splice(i, 1);
console.log("Splicing: " + JSON.stringify(arr));
}
}
return arr;
}
It may or may not be the most efficient, and should be entirely scalable. I've added some console.logs so you can see it as it works.
EDIT
In the interest of saving on the space the function used, I did that for loop at the end, but it seems likely that didn't properly find only unique results (depsite it passing my simple jsfiddle test). Please try replacing my for loop with the following:
var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
if (!checker || comparer(checker, arr[i]) != 0) {
checker = arr[i];
uniqueResults.push(checker);
}
}
return uniqueResults;
I use this function. its not doing any sorting, but produces result. Cant say about performance as never measure it.
var unique = function(a){
var seen = [], result = [];
for(var len = a.length, i = len-1; i >= 0; i--){
if(!seen[a[i]]){
seen[a[i]] = true;
result.push(a[i]);
}
}
return result;
}
var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"];
console.log(unique(ar));// this will produce [1,2,3,"", "a", "b"] all unique elements.
Below is Henrique Feijo's answer with ample explanation and an example that you can cut and paste:
Goal: Convert an array of objects that contains duplicate objects (like this one)...
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
... Into an array of objects without duplicate objects (like this one):
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
Explanation provided in the comments:
var allContent = [{
"id": 10620,
"name": "Things to Print"
}, {
"id": 10620,
"name": "Things to Print"
}, {
"id": 4334,
"name": "Interesting"
}]
//Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
for (i = 0, n = allContent.length; i < n; i++) {
var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
//Here, you create an object within the associative array that has a key composed of the two values from the original object.
// Use a delimiter to not have foo+bar handled like fo+obar
//Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed.
//The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
}
//Recontructs the list with only the unique objects left in the doDupeObj associative array
var i = 0;
var nonDuplicatedArray = [];
for (var item in noDupeObj) {
nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
}
console.log(nonDuplicatedArray)
For those who love ES6 and short stuff, here it's one solution:
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" }
];
Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Jon" },
{ title: "cry", artist: "Jon" }
];
const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
console.log(`New array length: ${unique.length}`)
console.log(unique)
The above example only works for a unique title or id. Basically, it creates a new map for songs with duplicate titles.
Below code compares object with JSON as String format and removes duplicates and works fine with simple arrays.
Array.prototype.unique=function(a){
return function(){
return this.filter(a)
}
}(
function(a,b,c){
var tmp=[];
c.forEach(function(el){
tmp.push(JSON.stringify(el))
});
return tmp.indexOf(JSON.stringify(a),b+1)<0
})
If you are using underscore js, it is easy to remove duplicate object.
http://underscorejs.org/#uniq
function remove_duplicates(objectsArray) {
var arr = [], collection = [];
$.each(objectsArray, function (index, value) {
if ($.inArray(value.id, arr) == -1) {
arr.push(value.id);
collection.push(value);
}
});
return collection;
}

How to replace item in array?

Each item of this array is some number:
var items = Array(523,3452,334,31, ...5346);
How to replace some item with a new one?
For example, we want to replace 3452 with 1010, how would we do this?
var index = items.indexOf(3452);
if (index !== -1) {
items[index] = 1010;
}
Also it is recommend you not use the constructor method to initialize your arrays. Instead, use the literal syntax:
var items = [523, 3452, 334, 31, 5346];
You can also use the ~ operator if you are into terse JavaScript and want to shorten the -1 comparison:
var index = items.indexOf(3452);
if (~index) {
items[index] = 1010;
}
Sometimes I even like to write a contains function to abstract this check and make it easier to understand what's going on. What's awesome is this works on arrays and strings both:
var contains = function (haystack, needle) {
return !!~haystack.indexOf(needle);
};
// can be used like so now:
if (contains(items, 3452)) {
// do something else...
}
Starting with ES6/ES2015 for strings, and proposed for ES2016 for arrays, you can more easily determine if a source contains another value:
if (haystack.includes(needle)) {
// do your thing
}
The Array.indexOf() method will replace the first instance. To get every instance use Array.map():
a = a.map(function(item) { return item == 3452 ? 1010 : item; });
Of course, that creates a new array. If you want to do it in place, use Array.forEach():
a.forEach(function(item, i) { if (item == 3452) a[i] = 1010; });
Answer from #gilly3 is great.
Replace object in an array, keeping the array order unchanged
I prefer the following way to update the new updated record into my array of records when I get data from the server. It keeps the order intact and quite straight forward one liner.
users = users.map(u => u.id !== editedUser.id ? u : editedUser);
var users = [
{id: 1, firstname: 'John', lastname: 'Ken'},
{id: 2, firstname: 'Robin', lastname: 'Hood'},
{id: 3, firstname: 'William', lastname: 'Cook'}
];
var editedUser = {id: 2, firstname: 'Michael', lastname: 'Angelo'};
users = users.map(u => u.id !== editedUser.id ? u : editedUser);
console.log('users -> ', users);
My suggested solution would be:
items.splice(1, 1, 1010);
The splice operation will start at index 1, remove 1 item in the array (i.e. 3452), and will replace it with the new item 1010.
Use indexOf to find an element.
var i = items.indexOf(3452);
items[i] = 1010;
First method
Best way in just one line to replace or update item of array
array.splice(array.indexOf(valueToReplace), 1, newValue)
Eg:
let items = ['JS', 'PHP', 'RUBY'];
let replacedItem = items.splice(items.indexOf('RUBY'), 1, 'PYTHON')
console.log(replacedItem) //['RUBY']
console.log(items) //['JS', 'PHP', 'PYTHON']
Second method
An other simple way to do the same operation is :
items[items.indexOf(oldValue)] = newValue
Easily accomplished with a for loop.
for (var i = 0; i < items.length; i++)
if (items[i] == 3452)
items[i] = 1010;
If using a complex object (or even a simple one) and you can use es6, Array.prototype.findIndex is a good one. For the OP's array, they could do,
const index = items.findIndex(x => x === 3452)
items[index] = 1010
For more complex objects, this really shines. For example,
const index =
items.findIndex(
x => x.jerseyNumber === 9 && x.school === 'Ohio State'
)
items[index].lastName = 'Utah'
items[index].firstName = 'Johnny'
You can edit any number of the list using indexes
for example :
items[0] = 5;
items[5] = 100;
ES6 way:
const items = Array(523, 3452, 334, 31, ...5346);
We wanna replace 3452 with 1010, solution:
const newItems = items.map(item => item === 3452 ? 1010 : item);
Surely, the question is for many years ago and for now I just prefer to use immutable solution, definitely, it is awesome for ReactJS.
For frequent usage I offer below function:
const itemReplacer = (array, oldItem, newItem) =>
array.map(item => item === oldItem ? newItem : item);
A functional approach to replacing an element of an array in javascript:
const replace = (array, index, ...items) => [...array.slice(0, index), ...items, ...array.slice(index + 1)];
The immutable way to replace the element in the list using ES6 spread operators and .slice method.
const arr = ['fir', 'next', 'third'], item = 'next'
const nextArr = [
...arr.slice(0, arr.indexOf(item)),
'second',
...arr.slice(arr.indexOf(item) + 1)
]
Verify that works
console.log(arr) // [ 'fir', 'next', 'third' ]
console.log(nextArr) // ['fir', 'second', 'third']
Replacement can be done in one line:
var items = Array(523, 3452, 334, 31, 5346);
items[items.map((e, i) => [i, e]).filter(e => e[1] == 3452)[0][0]] = 1010
console.log(items);
Or create a function to reuse:
Array.prototype.replace = function(t, v) {
if (this.indexOf(t)!= -1)
this[this.map((e, i) => [i, e]).filter(e => e[1] == t)[0][0]] = v;
};
//Check
var items = Array(523, 3452, 334, 31, 5346);
items.replace(3452, 1010);
console.log(items);
var items = Array(523,3452,334,31,5346);
If you know the value then use,
items[items.indexOf(334)] = 1010;
If you want to know that value is present or not, then use,
var point = items.indexOf(334);
if (point !== -1) {
items[point] = 1010;
}
If you know the place (position) then directly use,
items[--position] = 1010;
If you want replace few elements, and you know only starting position only means,
items.splice(2, 1, 1010, 1220);
for more about .splice
The easiest way is to use some libraries like underscorejs and map method.
var items = Array(523,3452,334,31,...5346);
_.map(items, function(num) {
return (num == 3452) ? 1010 : num;
});
=> [523, 1010, 334, 31, ...5346]
If you want a simple sugar sintax oneliner you can just:
(elements = elements.filter(element => element.id !== updatedElement.id)).push(updatedElement);
Like:
let elements = [ { id: 1, name: 'element one' }, { id: 2, name: 'element two'} ];
const updatedElement = { id: 1, name: 'updated element one' };
If you don't have id you could stringify the element like:
(elements = elements.filter(element => JSON.stringify(element) !== JSON.stringify(updatedElement))).push(updatedElement);
var index = Array.indexOf(Array value);
if (index > -1) {
Array.splice(index, 1);
}
from here you can delete a particular value from array and based on the same index
you can insert value in array .
Array.splice(index, 0, Array value);
Well if anyone is interresting on how to replace an object from its index in an array, here's a solution.
Find the index of the object by its id:
const index = items.map(item => item.id).indexOf(objectId)
Replace the object using Object.assign() method:
Object.assign(items[index], newValue)
items[items.indexOf(3452)] = 1010
great for simple swaps. try the snippet below
const items = Array(523, 3452, 334, 31, 5346);
console.log(items)
items[items.indexOf(3452)] = 1010
console.log(items)
Here is the basic answer made into a reusable function:
function arrayFindReplace(array, findValue, replaceValue){
while(array.indexOf(findValue) !== -1){
let index = array.indexOf(findValue);
array[index] = replaceValue;
}
}
Here's a one liner. It assumes the item will be in the array.
var items = [523, 3452, 334, 31, 5346]
var replace = (arr, oldVal, newVal) => (arr[arr.indexOf(oldVal)] = newVal, arr)
console.log(replace(items, 3452, 1010))
const items = Array(1, 2, 3, 4, 5);
console.log(items)
items[items.indexOf(2)] = 1010
console.log(items)
First, rewrite your array like this:
var items = [523,3452,334,31,...5346];
Next, access the element in the array through its index number. The formula to determine the index number is: n-1
To replace the first item (n=1) in the array, write:
items[0] = Enter Your New Number;
In your example, the number 3452 is in the second position (n=2). So the formula to determine the index number is 2-1 = 1. So write the following code to replace 3452 with 1010:
items[1] = 1010;
I solved this problem using for loops and iterating through the original array and adding the positions of the matching arreas to another array and then looping through that array and changing it in the original array then return it, I used and arrow function but a regular function would work too.
var replace = (arr, replaceThis, WithThis) => {
if (!Array.isArray(arr)) throw new RangeError("Error");
var itemSpots = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] == replaceThis) itemSpots.push(i);
}
for (var i = 0; i < itemSpots.length; i++) {
arr[itemSpots[i]] = WithThis;
}
return arr;
};
presentPrompt(id,productqty) {
let alert = this.forgotCtrl.create({
title: 'Test',
inputs: [
{
name: 'pickqty',
placeholder: 'pick quantity'
},
{
name: 'state',
value: 'verified',
disabled:true,
placeholder: 'state',
}
],
buttons: [
{
text: 'Ok',
role: 'cancel',
handler: data => {
console.log('dataaaaname',data.pickqty);
console.log('dataaaapwd',data.state);
for (var i = 0; i < this.cottonLists.length; i++){
if (this.cottonLists[i].id == id){
this.cottonLists[i].real_stock = data.pickqty;
}
}
for (var i = 0; i < this.cottonLists.length; i++){
if (this.cottonLists[i].id == id){
this.cottonLists[i].state = 'verified';
}
}
//Log object to console again.
console.log("After update: ", this.cottonLists)
console.log('Ok clicked');
}
},
]
});
alert.present();
}
As per your requirement you can change fields and array names.
thats all. Enjoy your coding.
The easiest way is this.
var items = Array(523,3452,334,31, 5346);
var replaceWhat = 3452, replaceWith = 1010;
if ( ( i = items.indexOf(replaceWhat) ) >=0 ) items.splice(i, 1, replaceWith);
console.log(items);
>>> (5) [523, 1010, 334, 31, 5346]
When your array have many old item to replace new item, you can use this way:
function replaceArray(array, oldItem, newItem) {
for (let i = 0; i < array.length; i++) {
const index = array.indexOf(oldItem);
if (~index) {
array[index] = newItem;
}
}
return array
}
console.log(replaceArray([1, 2, 3, 2, 2, 8, 1, 9], 2, 5));
console.log(replaceArray([1, 2, 3, 2, 2, 8, 1, 9], 2, "Hi"));
let items = Array(523,3452,334,31, 5346);
items[0]=1010;
This will do the job
Array.prototype.replace = function(a, b) {
return this.map(item => item == a ? b : item)
}
Usage:
let items = ['hi', 'hi', 'hello', 'hi', 'hello', 'hello', 'hi']
console.log(items.replace('hello', 'hi'))
Output:
['hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi']
The nice thing is, that EVERY array will have .replace() property.

Categories