Sorting object by value - javascript

I can imagine this has been asked a few times but I literally cannot find an example of a solution to the specific problem I'm trying to figure out.
So I have an object, like so:
var collection = [{ id: 0 }, { id: 1 }, { id: 2 }];
I then have an array, which is the 'order', like so:
var order = [2, 0, 1];
I want to use the 'order' array to reorder the collection in that specific order. I've been trying quite a few solutions with the .sort function, but I can't find one that fits. Can anyone enlighten me? Probably simple, I'm hoping.

You can use the sort() method to accomplish this using indexOf:
collection.sort(function(a, b){
return order.indexOf(a.id) > order.indexOf(b.id);
});

You can use indexOf function on the order array in the custom sort function, like this:
collection.sort(function(x, y) {
return order.indexOf(x.id) > order.indexOf(y.id);
});

seems to be as easy as that:
var collection = [{ id: 0 }, { id: 1 }, { id: 2 }];
var order = [2, 0, 1];
var sorted = [];
for(var i=0,c=order.length;i<c;i++){
sorted.push(collection[order[i]]);
}

Try that:
var collection = [{ id: 0 }, { id: 1 }, { id: 2 }];
var order = [2, 0, 1];
var sortedCollection = [];
for ( var i = 0; i < order.length; i++ )
sortedCollection.push(collection[order[i]]);
console.log(sortedCollection);

The thing you want to avoid here is scanning through either of these arrays more than you have to.
Here's one solution that avoids this:
/*
* Map the indexes of the objects in collection to their final location
*/
var sortIndex = {};
order.forEach(function(value, index) {
sortIndex[value] = index;
});
/*
* Put the objects in collection into their new, sorted collection
*/
var sortedCollection = [];
collection.forEach(function(value) {
var sortedLocation = sortIndex[value.id];
sortedCollection[sortedLocation] = value;
});
Thus, we have a single scan through each of the arrays, keeping the work down to a minimum.
I've used forEach here as a convenience; you could use a library like Lodash or Underscore, or rewrite this to use explicit iteration over the arrays.

Related

How to find element in array

I'm really new to javascript and I have an array of objects.
var cart = [
{ id: 1, price: 2 },
{ id: 2, price: 1 }
];
and I'm using a for loop to find the ID:
for (var i = 0; i < cart.length; i++) {
if (cart[i].id === id) {
return cart[i]
}
}
return null;
I know there's functions like find(), but I'm not too sure on how to use that. Can anyone help?
With find, you might need babel, but just the code you need:
ES6
const id = 1;
const found = cart.find(item => item.id === id)
Vanilla
var id = 1;
var found = cart.find(function(item) {return item.id === id})
find takes a function (in our case with es6: () => {} is an anonymous function), and applies it to every item in the list, until it finds the first match, how does it know when it is a match: once your function returns true, then it cuts the loop, and returns the item.
HOWEVER
Another option, that does not use find but might be more readable than a traditional for loop:
var id = 1;
for(var item in cart) {
if(item.id == id) {
return item;
}
}
return null
There are also a slew of libraries out there that can help you achieve this on different ways, ex: underscore.js, lodash, which I will not cover, but you can take a look at if you are really interested.
You are right. There is a function called find. You can set up the callback function to use with find, and even set it up to accept a parameter (such as the id):
var cart = [{
id: 1,
price: 2
}, {
id: 2,
price: 1
}];
function byID(id) {
return function(element) {
return element.id == id;
}
}
var item = cart.find(byID(2));
console.log(item);
With issues like this, I very much appreciate the library lodash. It allows you to do things like so:
var cart = [{id: 1, price: 5}, {id: 2, price: 6}];
var item = _.find(cart, {id:2});
console.log(item);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Eloquent Javascript A list returns undefined , Why?

Task
A list
Objects, as generic blobs of values, can be used to build all sorts of data structures. A common data structure is the list (not to be confused with the array). A list is a nested set of objects, with the first object holding a reference to the second, the second to the third, and so on.
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
The resulting objects form a chain, like this:
A linked list
A nice thing about lists is that they can share parts of their structure. For example, if I create two new values {value: 0, rest: list} and {value: -1, rest: list} (with list referring to the variable defined earlier), they are both independent lists, but they share the structure that makes up their last three elements. In addition, the original list is also still a valid three-element list.
Write a function arrayToList that builds up a data structure like the previous one when given [1, 2, 3]
The over all goal is to create a function that creates a list structure as mentioned in the Exercise "A list" in the Data Structures Chapter.
function arrayToList(array){
var list = {};
var i = 0;
var rest = ((i < array.length) ? (i++,{value: array[i],rest: rest}): null) ;
list = {
value: array[0],
rest: rest
}
return list;
}
What I was hoping to accomplish was use the rest variable to call itself until the var i was greater than the array length, but when I run the code the rest property return undefined... my question is why does it not behave as recursive call to the same ternary var rest. I am fairly new to javascript so any advise or articles that would help me understand why this happens would be great.
You can use Array.prototype.reduce()
var list = {};
var arr = [1,2,3];
arr.reduce((o, prop, index, array) => {
o["value"] = prop;
o["rest"] = index < array.length -1 ? {} : null;
return o["rest"]
}, list);
console.log(list);
As commenters have said, you aren't actually making this function recursive - it never calls itself.
To achieve what you want you'd have to do this:
function arrayToList(array){
var list = {};
var rest = ((array.length) ? (arrayToList(array.slice(1))) : null) ;
list = {
value: array[0],
rest: rest
}
return list;
}
In modern javascript, you can simply do this:
var arrayToList = array => array.slice().reverse().reduce((rest, value) => ({ value, rest }), { rest:null });
which, after running it through a transpiler, becomes
var arrayToList = function arrayToList(array) {
return array.slice().reverse().reduce(function (rest, value) {
return { value: value, rest: rest };
}, { rest:null });
};
var array = [1,2,3,4,5];
var arrayToList = function(array) {
var list = {};
var array = array.reverse();
var arrLength = array.length;
for (var i = 0; i < arrLength; i++) {
var newList = function() {
if (i) {
return list;
} else {
return null
}
}
list = {
value: array[i],
list: newList()
}
}
return list;
}
console.log(arrayToList(array));
I also learn javascript from that book and i struggled with this one.
I think this is more close to what we have learned in the book so far.
Using reduce function is also a great way to resolve this , but it is not yet presented in the book and we presume we have no idea how to use it.

How can I do reverse in a for loop?

I have this Js method but I can't to implement a reverse loop
$scope.organizeByMonth = function () {
for (var i in $scope.incidents) {
var month = new Date($scope.incidents[i].upload_date).getMonth();
if (!monthIncidents[month]) {
monthIncidents[month] = {
name: $scope.months[month],
incidents: []
};
}
var incident = $scope.incidents[i];
incident.index = i;
monthIncidents[month].incidents.push(incident);
}
};
how can I show the same objects in reverse :/
You can't do a reverse loop with the for in syntax - it also doesn't matter. Objects in JavaScript are unordered.
The closest to a reverse for..in.. loop would be to get the object keys and iterate backwards over them:
var object = { a: 1, b: 2, c: 3 };
var keys = Object.keys(object);
for(var i = keys.length - 1; i >= 0; i--) {
console.log(keys[i], object[keys[i]]);
}
http://jsfiddle.net/1oztmp2e/
Why would you like to reverse the for loop? Is it only for display purpose?
If so, I am assuming you are using ng-repeat for displaying the records. Should that be the case, you can use a custom reverse filter.
Or if you need the array reversed for a different reason, simply use Array.reverse after populating the array

map items of array to another array

I have an array:
arr = [ 1, 2 , 3 ]
And another array where i hold DOM elements as
Elem = [ 1, 3]
I need to iterate over arr and only do stuff if the index match. For example since I have elem 1 and 3 when I loop through arr something should only happen for 1 and 3 and 2 should be skipped since there is no elem 2.
Someone told me to look into associative arrays and I wonder how I can do this with the least number of lines.
I want the code to be simple and readable and so far all the examples of associative arrays make no sense and are bloated.
for(var i = 0;i<arr.length;i++){
if(Elem.indexOf(arr[i])>-1){
//Elem contains arr[i] (contains object that at index i in arr)
//will be called only for 1 and 3 in arr
arr[i] = ... //do what you want with this object.
}
}
Do you mean this?
I modified the second array a bit to allow defining multiple actions in one place. I am not sure if I understand you correctly.
// array of DOM objects available
var arr = ['object1-selector', 'object2-selector', 'object3-selector'];
// array of actions with items that the method should be applied to
var actions = [
{
items: ['object1-selector', 'object3-selector'],
perform: function(elem) {
alert(elem);
}
},
{
items: ['object2-selector'],
perform: function(elem) {
alert(elem);
}
},
{
items: ['object4-selector'],
perform: function(elem) {
alert(elem);
}
}
];
//forEach loop that iterates over actions and checks if selector exists.
//If yes - it invokes the method
actions.forEach(function(action) {
action.items.forEach(function(item) {
if(arr.indexOf(item) > -1) {
action.perform(item);
}
});
});
If you want to have actions defined in one place and objects in a multidimensional array - let me know. I will try to adjust the example. If you don't store selectors but whole DOM objects, just modify the items: array and loop, that checks if element exists.
Oh, and here is jsfiddle: http://jsfiddle.net/3WJxc/2/. jQuery used only for alert() to show you working example.
Not really sure how you identify the elements in the second array but this is my suggestion. Array with ids
var arr = [ "id_1", "id_2", "id_3" ]
var Elem = {
"id_1": html_element,
"id_2": html_element,
"id_3": html_element
}
Then all you need to do is
for( var i = 0; i < arr.length; i++ ) {
if( Elem[ arr[i] ] ) {
// do stuff
}
}

jQuery function to get all unique elements from an array?

jQuery.unique lets you get unique elements of an array, but the docs say the function is mostly for internal use and only operates on DOM elements. Another SO response said the unique() function worked on numbers, but that this use case is not necessarily future proof because it's not explicitly stated in the docs.
Given this, is there a "standard" jQuery function for accessing only the unique values — specifically, primitives like integers — in an array? (Obviously, we can construct a loop with the each() function, but we are new to jQuery and would like to know if there is a dedicated jQuery function for this.)
You can use array.filter to return the first item of each distinct value-
var a = [ 1, 5, 1, 6, 4, 5, 2, 5, 4, 3, 1, 2, 6, 6, 3, 3, 2, 4 ];
var unique = a.filter(function(itm, i, a) {
return i == a.indexOf(itm);
});
console.log(unique);
If supporting IE8 and below is primary, don't use the unsupported filter method.
Otherwise,
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun, scope) {
var T = this, A = [], i = 0, itm, L = T.length;
if (typeof fun == 'function') {
while(i < L) {
if (i in T) {
itm = T[i];
if (fun.call(scope, itm, i, T)) A[A.length] = itm;
}
++i;
}
}
return A;
}
}
Just use this code as the basis of a simple JQuery plugin.
$.extend({
distinct : function(anArray) {
var result = [];
$.each(anArray, function(i,v){
if ($.inArray(v, result) == -1) result.push(v);
});
return result;
}
});
Use as so:
$.distinct([0,1,2,2,3]);
Based on #kennebec's answer, but fixed for IE8 and below by using jQuery wrappers around the array to provide missing Array functions filter and indexOf:
$.makeArray() wrapper might not be absolutely needed, but you'll get odd results if you omit this wrapper and JSON.stringify the result otherwise.
var a = [1,5,1,6,4,5,2,5,4,3,1,2,6,6,3,3,2,4];
// note: jQuery's filter params are opposite of javascript's native implementation :(
var unique = $.makeArray($(a).filter(function(i,itm){
// note: 'index', not 'indexOf'
return i == $(a).index(itm);
}));
// unique: [1, 5, 6, 4, 2, 3]
I would use underscore.js, which provides a uniq method that does what you want.
// for numbers
a = [1,3,2,4,5,6,7,8, 1,1,4,5,6]
$.unique(a)
[7, 6, 1, 8, 3, 2, 5, 4]
// for string
a = ["a", "a", "b"]
$.unique(a)
["b", "a"]
And for dom elements there is no example is needed here I guess because you already know that!
Here is the jsfiddle link of live example:
http://jsfiddle.net/3BtMc/4/
Paul Irish has a "Duck Punching" method (see example 2) that modifies jQuery's $.unique() method to return unique elements of any type:
(function($){
var _old = $.unique;
$.unique = function(arr){
// do the default behavior only if we got an array of elements
if (!!arr[0].nodeType){
return _old.apply(this,arguments);
} else {
// reduce the array to contain no dupes via grep/inArray
return $.grep(arr,function(v,k){
return $.inArray(v,arr) === k;
});
}
};
})(jQuery);
Walk the array and push items into a hash as you come across them. Cross-reference the hash for each new element.
Note that this will ONLY work properly for primitives (strings, numbers, null, undefined, NaN) and a few objects that serialize to the same thing (functions, strings, dates, possibly arrays depending on content). Hashes in this will collide as they all serialize to the same thing, e.g. "[object Object]"
Array.prototype.distinct = function(){
var map = {}, out = [];
for(var i=0, l=this.length; i<l; i++){
if(map[this[i]]){ continue; }
out.push(this[i]);
map[this[i]] = 1;
}
return out;
}
There's also no reason you can't use jQuery.unique. The only thing I don't like about it is that it destroys the ordering of your array. Here's the exact code for it if you're interested:
Sizzle.uniqueSort = function(results){
if ( sortOrder ) {
hasDuplicate = baseHasDuplicate;
results.sort(sortOrder);
if ( hasDuplicate ) {
for ( var i = 1; i < results.length; i++ ) {
if ( results[i] === results[i-1] ) {
results.splice(i--, 1);
}
}
}
}
return results;
};
this is js1568's solution, modified to work on a generic array of objects, like:
var genericObject=[
{genProp:'this is a string',randomInt:10,isBoolean:false},
{genProp:'this is another string',randomInt:20,isBoolean:false},
{genProp:'this is a string',randomInt:10,isBoolean:true},
{genProp:'this is another string',randomInt:30,isBoolean:false},
{genProp:'this is a string',randomInt:40,isBoolean:true},
{genProp:'i like strings',randomInt:60,isBoolean:true},
{genProp:'this is a string',randomInt:70,isBoolean:true},
{genProp:'this string is unique',randomInt:50,isBoolean:true},
{genProp:'this is a string',randomInt:50,isBoolean:false},
{genProp:'i like strings',randomInt:70,isBoolean:false}
]
It accepts one more parameter called propertyName, guess! :)
$.extend({
distinctObj:function(obj,propertyName) {
var result = [];
$.each(obj,function(i,v){
var prop=eval("v."+propertyName);
if ($.inArray(prop, result) == -1) result.push(prop);
});
return result;
}
});
so, if you need to extract a list of unique values for a given property, for example the values used for randomInt property, use this:
$.distinctObj(genericObject,'genProp');
it returns an array like this:
["this is a string", "this is another string", "i like strings", "this string is unique"]
function array_unique(array) {
var unique = [];
for ( var i = 0 ; i < array.length ; ++i ) {
if ( unique.indexOf(array[i]) == -1 )
unique.push(array[i]);
}
return unique;
}
Plain JavaScript modern solution if you don't need IE support (Array.from is not supported in IE).
You can use combination of Set and Array.from.
const arr = [1, 1, 11, 2, 4, 2, 5, 3, 1];
const set = new Set(arr);
const uniqueArr = Array.from(set);
console.log(uniqueArr);
The Set object lets you store unique values of any type, whether primitive values or object references.
The Array.from() method creates a new Array instance from an array-like or iterable object.
Also Array.from() can be replaced with spread operator.
const arr = [1, 1, 11, 2, 4, 2, 5, 3, 1];
const set = new Set(arr);
const uniqueArr = [...set];
console.log(uniqueArr);
You can use a jQuery plugin called Array Utilities to get an array of unique items.
It can be done like this:
var distinctArray = $.distinct([1, 2, 2, 3])
distinctArray = [1,2,3]
If anyone is using knockoutjs try:
ko.utils.arrayGetDistinctValues()
BTW have look at all ko.utils.array* utilities.
As of jquery 3.0 you can use $.uniqueSort(ARRAY)
Example
array = ["1","2","1","2"]
$.uniqueSort(array)
=> ["1", "2"]
If you need to support IE 8 or earlier, you can use jQuery to accomplish this.
var a = [1,2,2,3,4,3,5];
var unique = $.grep(a, function (item, index) {
return index === $.inArray(item, a);
});

Categories