Sorting an array of arrays in swift 3 - arrays

var myArray: [[String]] =
[
["1", "picture1.png", "John", "Smith"],
["2", "picture2.png", "Mike", "Rutherford"],
]
How to sort myArray on first item ? second item ? ascending ? descending ?
Many Thanks

I would suggest you create a struct or class to package this related data together:
struct Person {
let id: Int
let picture: String // This should probably be a URL, NSImage or UIImage
let firstName: String
let lastName: String
}
And then define your instances with the correct types (e.g. the id is an Int, not a String representation of an Int.
let people = [
Person(
id: 1,
picture: "picture1.png",
firstName: "John",
lastName: "Smith"
),
Person(
id: 2,
picture: "picture2.png",
firstName: "Mike",
lastName: "Rutherford"
),
]
From there, you can sort it any which way you please:
people.sorted{ $0.id < $1.id }
people.sorted{ $0.id > $1.id }
people.sorted{ $0.picture < $1.picture }
people.sorted{ $0.picture > $1.picture }
people.sorted{ $0.firstName < $1.firstName }
people.sorted{ $0.firstName > $1.firstName }
people.sorted{ $0.lastName < $1.lastName }
people.sorted{ $0.lastName > $1.lastName }

Notice, that index ranged is not checked, what could lead to a fatal error at runtime. Check Alexanders comment! :)
var myArray: [[String]] =
[
["1", "picture1.png", "John", "Smith"],
["2", "picture2.png", "Mike", "Rutherford"],
]
func sort<T: Comparable>(ArrayOfArrays: [[T]], sortingIndex: Int, sortFunc: (T, T) -> Bool)) -> [[T]] {
return ArrayOfArrays.sorted {sortFunc($0[sortingIndex], $1[sortingIndex])}
}
}
print(sort(ArrayOfArrays: myArray, sortingIndex: 0, sortFunc: <))
//[["1", "picture1.png", "John", "Smith"], ["2", "picture2.png", "Mike", "Rutherford"]]
print(sort(ArrayOfArrays: myArray, sortingIndex: 0, sortFunc: >))
//[["2", "picture2.png", "Mike", "Rutherford"], ["1", "picture1.png", "John", "Smith"]]

Swift's Array has a built-in sort function. Just call it.
myArray[0].sort { $0.compare($1, options: .numeric) == .orderedAscending }
myArray[1].sort { $0.compare($1, options: .numeric) == .orderedDescending }

To sort the arrays using numeric string comparison (e.g. where "2" < "10") of the item at a particular index:
let index = 1 // sort by second entry
myArray.sort { $0[index].compare($1[index], options: .numeric) == .orderedAscending }
If you don't need numeric comparisons (e.g. where "10" < "2"):
myArray.sort { $0[index] < $1[index] }
As others point out, though, you really should be using arrays of custom struct or class rather than representing your objects as mere array of strings.

Related

How to get the row and column index in ForEach loop of a 2D array in swiftUI?

I would like to get the row and column index in ForEach loop of a 2D array in swiftUI.
let array = [
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"]
]
ForEach(array, id: .self) { row in
ForEach(row, id: \.self) { element in
// How to get the index i and j for row and column of a 2D array in swiftUI ???
}
}
Since you have an array of [String] you must have 2 ForEach:
ForEach(Array(row.enumerated()), id:\.self) { i, array in
ForEach(Array(array.enumerated()), id: \.self) { j, element in
//do something
}
}
try something like this, ...to get the index i and j for row and column of a 2D array...:
struct ContentView: View {
let array = [["1", "2", "3"],["4", "5", "6"],["7", "8", "9"]]
var body: some View {
ForEach(array.indices, id: \.self) { i in
ForEach(array[i].indices, id: \.self) { j in
Text(array[i][j])
}
}
}
}

Next JS how to order the price of products using two buttons receiving data? [duplicate]

I've got the following objects using AJAX and stored them in an array:
var homes = [
{
"h_id": "3",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "162500"
}, {
"h_id": "4",
"city": "Bevery Hills",
"state": "CA",
"zip": "90210",
"price": "319250"
}, {
"h_id": "5",
"city": "New York",
"state": "NY",
"zip": "00010",
"price": "962500"
}
];
How do I create a function to sort the objects by the price property in ascending or descending order using JavaScript only?
Sort homes by price in ascending order:
homes.sort(function(a, b) {
return parseFloat(a.price) - parseFloat(b.price);
});
Or after ES6 version:
homes.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
Some documentation can be found here.
For descending order, you may use
homes.sort((a, b) => parseFloat(b.price) - parseFloat(a.price));
Here's a more flexible version, which allows you to create
reusable sort functions, and sort by any field.
const sort_by = (field, reverse, primer) => {
const key = primer ?
function(x) {
return primer(x[field])
} :
function(x) {
return x[field]
};
reverse = !reverse ? 1 : -1;
return function(a, b) {
return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
}
}
//Now you can sort by any field at will...
const homes=[{h_id:"3",city:"Dallas",state:"TX",zip:"75201",price:"162500"},{h_id:"4",city:"Bevery Hills",state:"CA",zip:"90210",price:"319250"},{h_id:"5",city:"New York",state:"NY",zip:"00010",price:"962500"}];
// Sort by price high to low
console.log(homes.sort(sort_by('price', true, parseInt)));
// Sort by city, case-insensitive, A-Z
console.log(homes.sort(sort_by('city', false, (a) => a.toUpperCase()
)));
To sort it you need to create a comparator function taking two arguments. Then call the sort function with that comparator function as follows:
// a and b are object elements of your array
function mycomparator(a,b) {
return parseInt(a.price, 10) - parseInt(b.price, 10);
}
homes.sort(mycomparator);
If you want to sort ascending switch the expressions on each side of the minus sign.
for string sorting in case some one needs it,
const dataArr = {
"hello": [{
"id": 114,
"keyword": "zzzzzz",
"region": "Sri Lanka",
"supportGroup": "administrators",
"category": "Category2"
}, {
"id": 115,
"keyword": "aaaaa",
"region": "Japan",
"supportGroup": "developers",
"category": "Category2"
}]
};
const sortArray = dataArr['hello'];
console.log(sortArray.sort((a, b) => {
if (a.region < b.region)
return -1;
if (a.region > b.region)
return 1;
return 0;
}));
If you have an ES6 compliant browser you can use:
Arrow functions
Number() function
sort() function
The difference between ascending and descending sort order is the sign of the value returned by your compare function:
var ascending = homes.sort((a, b) => Number(a.price) - Number(b.price));
var descending = homes.sort((a, b) => Number(b.price) - Number(a.price));
Here's a working code snippet:
var homes = [{
"h_id": "3",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "162500"
}, {
"h_id": "4",
"city": "Bevery Hills",
"state": "CA",
"zip": "90210",
"price": "319250"
}, {
"h_id": "5",
"city": "New York",
"state": "NY",
"zip": "00010",
"price": "962500"
}];
homes.sort((a, b) => Number(a.price) - Number(b.price));
console.log("ascending", homes);
homes.sort((a, b) => Number(b.price) - Number(a.price));
console.log("descending", homes);
I recommend GitHub: Array sortBy - a best implementation of sortBy method which uses the Schwartzian transform
But for now we are going to try this approach Gist: sortBy-old.js.
Let's create a method to sort arrays being able to arrange objects by some property.
Creating the sorting function
var sortBy = (function () {
var toString = Object.prototype.toString,
// default parser function
parse = function (x) { return x; },
// gets the item to be sorted
getItem = function (x) {
var isObject = x != null && typeof x === "object";
var isProp = isObject && this.prop in x;
return this.parser(isProp ? x[this.prop] : x);
};
/**
* Sorts an array of elements.
*
* #param {Array} array: the collection to sort
* #param {Object} cfg: the configuration options
* #property {String} cfg.prop: property name (if it is an Array of objects)
* #property {Boolean} cfg.desc: determines whether the sort is descending
* #property {Function} cfg.parser: function to parse the items to expected type
* #return {Array}
*/
return function sortby (array, cfg) {
if (!(array instanceof Array && array.length)) return [];
if (toString.call(cfg) !== "[object Object]") cfg = {};
if (typeof cfg.parser !== "function") cfg.parser = parse;
cfg.desc = !!cfg.desc ? -1 : 1;
return array.sort(function (a, b) {
a = getItem.call(cfg, a);
b = getItem.call(cfg, b);
return cfg.desc * (a < b ? -1 : +(a > b));
});
};
}());
Setting unsorted data
var data = [
{date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "Tab"},
{date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
{date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
{date: "2011-11-31T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "Visa"},
{date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-01T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "Cash"}
];
Using it
Arrange the array, by "date" as String
// sort by #date (ascending)
sortBy(data, { prop: "date" });
// expected: first element
// { date: "2011-11-01T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab" }
// expected: last element
// { date: "2011-11-31T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "Visa"}
If you want to ignore case sensitive, set the parser callback:
// sort by #type (ascending) IGNORING case-sensitive
sortBy(data, {
prop: "type",
parser: (t) => t.toUpperCase()
});
// expected: first element
// { date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "Cash" }
// expected: last element
// { date: "2011-11-31T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "Visa" }
If you want to convert the "date" field as Date type:
// sort by #date (descending) AS Date object
sortBy(data, {
prop: "date",
desc: true,
parser: (d) => new Date(d)
});
// expected: first element
// { date: "2011-11-31T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "Visa"}
// expected: last element
// { date: "2011-11-01T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab" }
Here you can play with the code:
jsbin.com/lesebi
Thanks to #Ozesh by his feedback, the issue related to properties with falsy values was fixed.
You want to sort it in Javascript, right? What you want is the sort() function. In this case you need to write a comparator function and pass it to sort(), so something like this:
function comparator(a, b) {
return parseInt(a["price"], 10) - parseInt(b["price"], 10);
}
var json = { "homes": [ /* your previous data */ ] };
console.log(json["homes"].sort(comparator));
Your comparator takes one of each of the nested hashes inside the array and decides which one is higher by checking the "price" field.
Use lodash.sortBy, (instructions using commonjs, you can also just put the script include-tag for the cdn at the top of your html)
var sortBy = require('lodash.sortby');
// or
sortBy = require('lodash').sortBy;
Descending order
var descendingOrder = sortBy( homes, 'price' ).reverse();
Ascending order
var ascendingOrder = sortBy( homes, 'price' );
I'm little late for the party but below is my logic for sorting.
function getSortedData(data, prop, isAsc) {
return data.sort((a, b) => {
return (a[prop] < b[prop] ? -1 : 1) * (isAsc ? 1 : -1)
});
}
You can use string1.localeCompare(string2) for string comparison
this.myArray.sort((a,b) => {
return a.stringProp.localeCompare(b.stringProp);
});
Note that localCompare is case insensitive
This could have been achieved through a simple one line valueof() sort function. Run code snippet below to see demo.
var homes = [
{
"h_id": "3",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "162500"
}, {
"h_id": "4",
"city": "Bevery Hills",
"state": "CA",
"zip": "90210",
"price": "319250"
}, {
"h_id": "5",
"city": "New York",
"state": "NY",
"zip": "00010",
"price": "962500"
}
];
console.log("To sort descending/highest first, use operator '<'");
homes.sort(function(a,b) { return a.price.valueOf() < b.price.valueOf();});
console.log(homes);
console.log("To sort ascending/lowest first, use operator '>'");
homes.sort(function(a,b) { return a.price.valueOf() > b.price.valueOf();});
console.log(homes);
Descending order of price:
homes.sort((x,y) => {return y.price - x.price})
Ascending order of price:
homes.sort((x,y) => {return x.price - y.price})
While I am aware that the OP wanted to sort an array of numbers, this question has been marked as the answer for similar questions regarding strings. To that fact, the above answers do not consider sorting an array of text where casing is important. Most answers take the string values and convert them to uppercase/lowercase and then sort one way or another. The requirements that I adhere to are simple:
Sort alphabetically A-Z
Uppercase values of the same word should come before lowercase values
Same letter (A/a, B/b) values should be grouped together
What I expect is [ A, a, B, b, C, c ] but the answers above return A, B, C, a, b, c. I actually scratched my head on this for longer than I wanted (which is why I am posting this in hopes that it will help at least one other person). While two users mention the localeCompare function in the comments for the marked answer, I didn't see that until after I stumbled upon the function while searching around. After reading the String.prototype.localeCompare() documentation I was able to come up with this:
var values = [ "Delta", "charlie", "delta", "Charlie", "Bravo", "alpha", "Alpha", "bravo" ];
var sorted = values.sort((a, b) => a.localeCompare(b, undefined, { caseFirst: "upper" }));
// Result: [ "Alpha", "alpha", "Bravo", "bravo", "Charlie", "charlie", "Delta", "delta" ]
This tells the function to sort uppercase values before lowercase values. The second parameter in the localeCompare function is to define the locale but if you leave it as undefined it automatically figures out the locale for you.
This works the same for sorting an array of objects as well:
var values = [
{ id: 6, title: "Delta" },
{ id: 2, title: "charlie" },
{ id: 3, title: "delta" },
{ id: 1, title: "Charlie" },
{ id: 8, title: "Bravo" },
{ id: 5, title: "alpha" },
{ id: 4, title: "Alpha" },
{ id: 7, title: "bravo" }
];
var sorted = values
.sort((a, b) => a.title.localeCompare(b.title, undefined, { caseFirst: "upper" }));
Here is a culmination of all answers above.
Fiddle validation: http://jsfiddle.net/bobberino/4qqk3/
var sortOn = function (arr, prop, reverse, numeric) {
// Ensure there's a property
if (!prop || !arr) {
return arr
}
// Set up sort function
var sort_by = function (field, rev, primer) {
// Return the required a,b function
return function (a, b) {
// Reset a, b to the field
a = primer(a[field]), b = primer(b[field]);
// Do actual sorting, reverse as needed
return ((a < b) ? -1 : ((a > b) ? 1 : 0)) * (rev ? -1 : 1);
}
}
// Distinguish between numeric and string to prevent 100's from coming before smaller
// e.g.
// 1
// 20
// 3
// 4000
// 50
if (numeric) {
// Do sort "in place" with sort_by function
arr.sort(sort_by(prop, reverse, function (a) {
// - Force value to a string.
// - Replace any non numeric characters.
// - Parse as float to allow 0.02 values.
return parseFloat(String(a).replace(/[^0-9.-]+/g, ''));
}));
} else {
// Do sort "in place" with sort_by function
arr.sort(sort_by(prop, reverse, function (a) {
// - Force value to string.
return String(a).toUpperCase();
}));
}
}
For sorting a array you must define a comparator function. This function always be different on your desired sorting pattern or order(i.e. ascending or descending).
Let create some functions that sort an array ascending or descending and that contains object or string or numeric values.
function sorterAscending(a,b) {
return a-b;
}
function sorterDescending(a,b) {
return b-a;
}
function sorterPriceAsc(a,b) {
return parseInt(a['price']) - parseInt(b['price']);
}
function sorterPriceDes(a,b) {
return parseInt(b['price']) - parseInt(b['price']);
}
Sort numbers (alphabetically and ascending):
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort();
Sort numbers (alphabetically and descending):
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort();
fruits.reverse();
Sort numbers (numerically and ascending):
var points = [40,100,1,5,25,10];
points.sort(sorterAscending());
Sort numbers (numerically and descending):
var points = [40,100,1,5,25,10];
points.sort(sorterDescending());
As above use sorterPriceAsc and sorterPriceDes method with your array with desired key.
homes.sort(sorterPriceAsc()) or homes.sort(sorterPriceDes())
You can use the JavaScript sort method with a callback function:
function compareASC(homeA, homeB)
{
return parseFloat(homeA.price) - parseFloat(homeB.price);
}
function compareDESC(homeA, homeB)
{
return parseFloat(homeB.price) - parseFloat(homeA.price);
}
// Sort ASC
homes.sort(compareASC);
// Sort DESC
homes.sort(compareDESC);
I also worked with some kind of rating and multiple fields sort:
arr = [
{type:'C', note:834},
{type:'D', note:732},
{type:'D', note:008},
{type:'F', note:474},
{type:'P', note:283},
{type:'P', note:165},
{type:'X', note:173},
{type:'Z', note:239},
];
arr.sort(function(a,b){
var _a = ((a.type==='C')?'0':(a.type==='P')?'1':'2');
_a += (a.type.localeCompare(b.type)===-1)?'0':'1';
_a += (a.note>b.note)?'1':'0';
var _b = ((b.type==='C')?'0':(b.type==='P')?'1':'2');
_b += (b.type.localeCompare(a.type)===-1)?'0':'1';
_b += (b.note>a.note)?'1':'0';
return parseInt(_a) - parseInt(_b);
});
Result
[
{"type":"C","note":834},
{"type":"P","note":165},
{"type":"P","note":283},
{"type":"D","note":8},
{"type":"D","note":732},
{"type":"F","note":474},
{"type":"X","note":173},
{"type":"Z","note":239}
]
While it is a bit of an overkill for just sorting a single array, this prototype function allows to sort Javascript arrays by any key, in ascending or descending order, including nested keys, using dot syntax.
(function(){
var keyPaths = [];
var saveKeyPath = function(path) {
keyPaths.push({
sign: (path[0] === '+' || path[0] === '-')? parseInt(path.shift()+1) : 1,
path: path
});
};
var valueOf = function(object, path) {
var ptr = object;
for (var i=0,l=path.length; i<l; i++) ptr = ptr[path[i]];
return ptr;
};
var comparer = function(a, b) {
for (var i = 0, l = keyPaths.length; i < l; i++) {
aVal = valueOf(a, keyPaths[i].path);
bVal = valueOf(b, keyPaths[i].path);
if (aVal > bVal) return keyPaths[i].sign;
if (aVal < bVal) return -keyPaths[i].sign;
}
return 0;
};
Array.prototype.sortBy = function() {
keyPaths = [];
for (var i=0,l=arguments.length; i<l; i++) {
switch (typeof(arguments[i])) {
case "object": saveKeyPath(arguments[i]); break;
case "string": saveKeyPath(arguments[i].match(/[+-]|[^.]+/g)); break;
}
}
return this.sort(comparer);
};
})();
Usage:
var data = [
{ name: { first: 'Josh', last: 'Jones' }, age: 30 },
{ name: { first: 'Carlos', last: 'Jacques' }, age: 19 },
{ name: { first: 'Carlos', last: 'Dante' }, age: 23 },
{ name: { first: 'Tim', last: 'Marley' }, age: 9 },
{ name: { first: 'Courtney', last: 'Smith' }, age: 27 },
{ name: { first: 'Bob', last: 'Smith' }, age: 30 }
]
data.sortBy('age'); // "Tim Marley(9)", "Carlos Jacques(19)", "Carlos Dante(23)", "Courtney Smith(27)", "Josh Jones(30)", "Bob Smith(30)"
Sorting by nested properties with dot-syntax or array-syntax:
data.sortBy('name.first'); // "Bob Smith(30)", "Carlos Dante(23)", "Carlos Jacques(19)", "Courtney Smith(27)", "Josh Jones(30)", "Tim Marley(9)"
data.sortBy(['name', 'first']); // "Bob Smith(30)", "Carlos Dante(23)", "Carlos Jacques(19)", "Courtney Smith(27)", "Josh Jones(30)", "Tim Marley(9)"
Sorting by multiple keys:
data.sortBy('name.first', 'age'); // "Bob Smith(30)", "Carlos Jacques(19)", "Carlos Dante(23)", "Courtney Smith(27)", "Josh Jones(30)", "Tim Marley(9)"
data.sortBy('name.first', '-age'); // "Bob Smith(30)", "Carlos Dante(23)", "Carlos Jacques(19)", "Courtney Smith(27)", "Josh Jones(30)", "Tim Marley(9)"
You can fork the repo: https://github.com/eneko/Array.sortBy
With ECMAScript 6 StoBor's answer can be done even more concise:
homes.sort((a, b) => a.price - b.price)
use this function
const r_sort = (a, b, field, asc) => {
let reverse = asc ? 1 : -1;
if (a[field] > b[field]) {
return 1 * reverse;
}
else if (b[field] > a[field]) {
return -1 * reverse;
}
else {
return 0;
} }
//usage:
homes = homes.sort((a,b) => r_sort(a,b,price,true)) // true for ascending and false for descending
For a normal array of elements values only:
function sortArrayOfElements(arrayToSort) {
function compareElements(a, b) {
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
return arrayToSort.sort(compareElements);
}
e.g. 1:
var array1 = [1,2,545,676,64,2,24]
output : [1, 2, 2, 24, 64, 545, 676]
var array2 = ["v","a",545,676,64,2,"24"]
output: ["a", "v", 2, "24", 64, 545, 676]
For an array of objects:
function sortArrayOfObjects(arrayToSort, key) {
function compareObjects(a, b) {
if (a[key] < b[key])
return -1;
if (a[key] > b[key])
return 1;
return 0;
}
return arrayToSort.sort(compareObjects);
}
e.g. 1: var array1= [{"name": "User4", "value": 4},{"name": "User3", "value": 3},{"name": "User2", "value": 2}]
output : [{"name": "User2", "value": 2},{"name": "User3", "value": 3},{"name": "User4", "value": 4}]
A more LINQ like solution:
Array.prototype.orderBy = function (selector, desc = false) {
return [...this].sort((a, b) => {
a = selector(a);
b = selector(b);
if (a == b) return 0;
return (desc ? a > b : a < b) ? -1 : 1;
});
}
Advantages:
autocompletion for properties
extends array prototype
does not change array
easy to use in method chaining
Usage:
Array.prototype.orderBy = function(selector, desc = false) {
return [...this].sort((a, b) => {
a = selector(a);
b = selector(b);
if (a == b) return 0;
return (desc ? a > b : a < b) ? -1 : 1;
});
};
var homes = [{
"h_id": "3",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "162500"
}, {
"h_id": "4",
"city": "Bevery Hills",
"state": "CA",
"zip": "90210",
"price": "319250"
}, {
"h_id": "5",
"city": "New York",
"state": "NY",
"zip": "00010",
"price": "962500"
}];
let sorted_homes = homes.orderBy(h => parseFloat(h.price));
console.log("sorted by price", sorted_homes);
let sorted_homes_desc = homes.orderBy(h => h.city, true);
console.log("sorted by City descending", sorted_homes_desc);
If you use Underscore.js, try sortBy:
// price is of an integer type
_.sortBy(homes, "price");
// price is of a string type
_.sortBy(homes, function(home) {return parseInt(home.price);});
Here is a slightly modified version of elegant implementation from the book "JavaScript: The Good Parts".
NOTE: This version of by is stable. It preserves the order of the first sort while performing the next chained sort.
I have added isAscending parameter to it. Also converted it to ES6 standards and "newer" good parts as recommended by the author.
You can sort ascending as well as descending and chain sort by multiple properties.
const by = function (name, minor, isAscending=true) {
const reverseMutliplier = isAscending ? 1 : -1;
return function (o, p) {
let a, b;
let result;
if (o && p && typeof o === "object" && typeof p === "object") {
a = o[name];
b = p[name];
if (a === b) {
return typeof minor === 'function' ? minor(o, p) : 0;
}
if (typeof a === typeof b) {
result = a < b ? -1 : 1;
} else {
result = typeof a < typeof b ? -1 : 1;
}
return result * reverseMutliplier;
} else {
throw {
name: "Error",
message: "Expected an object when sorting by " + name
};
}
};
};
let s = [
{first: 'Joe', last: 'Besser'},
{first: 'Moe', last: 'Howard'},
{first: 'Joe', last: 'DeRita'},
{first: 'Shemp', last: 'Howard'},
{first: 'Larry', last: 'Fine'},
{first: 'Curly', last: 'Howard'}
];
// Sort by: first ascending, last ascending
s.sort(by("first", by("last")));
console.log("Sort by: first ascending, last ascending: ", s); // "[
// {"first":"Curly","last":"Howard"},
// {"first":"Joe","last":"Besser"}, <======
// {"first":"Joe","last":"DeRita"}, <======
// {"first":"Larry","last":"Fine"},
// {"first":"Moe","last":"Howard"},
// {"first":"Shemp","last":"Howard"}
// ]
// Sort by: first ascending, last descending
s.sort(by("first", by("last", 0, false)));
console.log("sort by: first ascending, last descending: ", s); // "[
// {"first":"Curly","last":"Howard"},
// {"first":"Joe","last":"DeRita"}, <========
// {"first":"Joe","last":"Besser"}, <========
// {"first":"Larry","last":"Fine"},
// {"first":"Moe","last":"Howard"},
// {"first":"Shemp","last":"Howard"}
// ]
Create a function and sort based on the input using below code
var homes = [{
"h_id": "3",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "162500"
}, {
"h_id": "4",
"city": "Bevery Hills",
"state": "CA",
"zip": "90210",
"price": "319250"
}, {
"h_id": "5",
"city": "New York",
"state": "NY",
"zip": "00010",
"price": "962500"
}];
function sortList(list,order){
if(order=="ASC"){
return list.sort((a,b)=>{
return parseFloat(a.price) - parseFloat(b.price);
})
}
else{
return list.sort((a,b)=>{
return parseFloat(b.price) - parseFloat(a.price);
});
}
}
sortList(homes,'DESC');
console.log(homes);
For sort on multiple array object field.
Enter your field name in arrprop array like ["a","b","c"]
then pass in second parameter arrsource actual source we want to sort.
function SortArrayobject(arrprop,arrsource){
arrprop.forEach(function(i){
arrsource.sort(function(a,b){
return ((a[i] < b[i]) ? -1 : ((a[i] > b[i]) ? 1 : 0));
});
});
return arrsource;
}
You will need two function
function desc(a, b) {
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
}
function asc(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
Then you can apply this to any object property:
data.sort((a, b) => desc(parseFloat(a.price), parseFloat(b.price)));
let data = [
{label: "one", value:10},
{label: "two", value:5},
{label: "three", value:1},
];
// sort functions
function desc(a, b) {
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
}
function asc(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
// DESC
data.sort((a, b) => desc(a.value, b.value));
document.body.insertAdjacentHTML(
'beforeend',
'<strong>DESCending sorted</strong><pre>' + JSON.stringify(data) +'</pre>'
);
// ASC
data.sort((a, b) => asc(a.value, b.value));
document.body.insertAdjacentHTML(
'beforeend',
'<strong>ASCending sorted</strong><pre>' + JSON.stringify(data) +'</pre>'
);
Array.prototype.sortBy = function(callback) {
return this.sort((a, b) => callback(a) - callback(b))
}
[1,2,3,2].sortBy(i => i) // [1, 2, 2, 3]
[1,2,3,2].sortBy(i => i == 2) // [1, 3, 2, 2]
I recently wrote a universal function to manage this for you if you want to use it.
/**
* Sorts an object into an order
*
* #require jQuery
*
* #param object Our JSON object to sort
* #param type Only alphabetical at the moment
* #param identifier The array or object key to sort by
* #param order Ascending or Descending
*
* #returns Array
*/
function sortItems(object, type, identifier, order){
var returnedArray = [];
var emptiesArray = []; // An array for all of our empty cans
// Convert the given object to an array
$.each(object, function(key, object){
// Store all of our empty cans in their own array
// Store all other objects in our returned array
object[identifier] == null ? emptiesArray.push(object) : returnedArray.push(object);
});
// Sort the array based on the type given
switch(type){
case 'alphabetical':
returnedArray.sort(function(a, b){
return(a[identifier] == b[identifier]) ? 0 : (
// Sort ascending or descending based on order given
order == 'asc' ? a[identifier] > b[identifier] : a[identifier] < b[identifier]
) ? 1 : -1;
});
break;
default:
}
// Return our sorted array along with the empties at the bottom depending on sort order
return order == 'asc' ? returnedArray.concat(emptiesArray) : emptiesArray.concat(returnedArray);
}
homes.sort(function(a, b){
var nameA=a.prices.toLowerCase(), nameB=b.prices.toLowerCase()
if (nameA < nameB) //sort string ascending
return -1
if (nameA > nameB)
return 1
return 0 //default return value (no sorting)
})

Swift sort an array with strings and numbers [duplicate]

This question already has answers here:
Sorting array alphabetically with number
(8 answers)
Closed 5 years ago.
I have an array of strings,
let array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ]
I would like to get the output sorted in ascending as,
let sorted = [ "1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA" ]
I tried using the sorted command but it does not work when it encounters more than 2 digits e.g.: 100, 101, 200 etc.
array.sorted { $0? < $1? }
What would be the simple way to get this?
Xcode 11.3 • Swift 5.2 or later
You can use String method localizedStandardCompare (diacritics and case insensitive):
let array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ]
let sorted = array.sorted {$0.localizedStandardCompare($1) == .orderedAscending}
print(sorted) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"]
or using the method sort(by:) on a MutableCollection:
var array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ]
array.sort {$0.localizedStandardCompare($1) == .orderedAscending}
print(array) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"]
You can also implement your own localized standard sort method extending Collection:
public extension Sequence where Element: StringProtocol {
func localizedStandardSorted(ascending: Bool = true) -> [Element] {
let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending
return sorted { $0.localizedStandardCompare($1) == result }
}
}
let array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ]
let sorted = array.localizedStandardSorted()
print(sorted) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"]
The mutating method as well extending MutableCollection:
public extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {
mutating func localizedStandardSort(ascending: Bool = true) {
let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending
return sort { $0.localizedStandardCompare($1) == result }
}
}
var array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ]
array.localizedStandardSort()
print(array) // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"]
If you need to sort your array numerically you can use String compare method setting the options parameter to .numeric:
public extension Sequence where Element: StringProtocol {
func sortedNumerically(ascending: Bool = true) -> [Element] {
let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending
return sorted { $0.compare($1, options: .numeric) == result }
}
}
public extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {
mutating func sortNumerically(ascending: Bool = true) {
let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending
return sort { $0.compare($1, options: .numeric) == result }
}
}
var numbers = ["1.5","0.5","1"]
let sortedNumbers = numbers.sortedNumerically()
print(sortedNumbers) // ["0.5", "1", "1.5"]
print(numbers) // ["1.5","0.5","1"]
// mutating the original collection
numbers.sortNumerically(ascending: false)
print(numbers) // "["1.5", "1", "0.5"]\n"
To sort a custom class/structure by one of its properties:
extension MutableCollection where Self: RandomAccessCollection {
public mutating func localizedStandardSort<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) {
sort {
predicate($0).localizedStandardCompare(predicate($1)) ==
(ascending ? .orderedAscending : .orderedDescending)
}
}
}
public extension Sequence {
func localizedStandardSorted<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) -> [Element] {
sorted {
predicate($0).localizedStandardCompare(predicate($1)) ==
(ascending ? .orderedAscending : .orderedDescending)
}
}
}
public extension Sequence {
func sortedNumerically<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) -> [Element] {
let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending
return sorted { predicate($0).compare(predicate($1), options: .numeric) == result }
}
}
public extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {
mutating func sortNumerically<T: StringProtocol>(_ predicate: (Element) -> T, ascending: Bool = true) {
let result: ComparisonResult = ascending ? .orderedAscending : .orderedDescending
return sort { predicate($0).compare(predicate($1), options: .numeric) == result }
}
}
Playground testing
struct Person {
let name: String
let age : Int
}
extension Person : CustomStringConvertible {
var description: String { "name: \(name), age: \(age)" }
}
var people: [Person] = [.init(name: "Éd Sheeran", age: 26),
.init(name: "phil Collins", age: 66),
.init(name: "Shakira", age: 40),
.init(name: "rihanna", age: 25),
.init(name: "Bono", age: 57)]
let sorted = people.localizedStandardSorted(\.name)
print(sorted) // [name: Bono, age: 57, name: Éd Sheeran, age: 26, name: phil Collins, age: 66, name: rihanna, age: 25, name: Shakira, age: 40]
edit/update: Xcode 12 • Swift 5.5 or later
You can use KeyPathComparator and pass localizedStandard as the Comparator:
people.sort(using: KeyPathComparator(\.name, comparator: .localizedStandard))
print(people) // [name: Bono, age: 57, name: Éd Sheeran, age: 26, name: phil Collins, age: 66, name: rihanna, age: 25, name: Shakira, age: 40]
people.sort(using: KeyPathComparator(\.name, comparator: .localizedStandard, order: .reverse))
print(people) // "[name: Shakira, age: 40, name: rihanna, age: 25, name: phil Collins, age: 66, name: Éd Sheeran, age: 26, name: Bono, age: 57]"
For sorting just an array of strings you can also use KeyPathComparator and pass self for the KeyPath:
var array = [ "10", "1", "101", "NA", "100", "20", "210", "200", "NA", "7" ]
array.sort(using: KeyPathComparator(\.self, comparator: .localizedStandard))
array // ["1", "7", "10", "20", "100", "101", "200", "210", "NA", "NA"]
array.sort(using: KeyPathComparator(\.self, comparator: .localizedStandard, order: .reverse))
array // ["NA", "NA", "210", "200", "101", "100", "20", "10", "7", "1"]

How to delete brackets from array, but keep quotes

I have an array, with even number of elements:
var peoples = [
["1", "Adam", "Jones"],
["2", "Michael", "Jordan"],
["3", "Frank", "Forman"],
["4", "John", "Bryant"],
["5", "James", "Johnson"],
["6", "Vincent", "Carter"],
["7", "George", "Williams"],
["8", "Brandon", "Clarkson"]
];
and I’m trying to merge arrays in pairs by following pattern:
["1", "Adam", "Jones", "2", "Michael", "Jordan"]
["3", "Frank", "Forman","4", "John", "Bryant"]
etc.
I have a problem with following code:
for (var i = 0; i < peoples.length / 2; i++) {
array1[i].push(array2[i].join(","))
}
which is generating that result:
["1","Adam","Jones","2,Michael,Jordan"]
and it should be:
["1","Adam","Jones","2","Michael","Jordan"]
Here is my jsfiddle https://jsfiddle.net/danny3b/k5hza694/
I've already done it by myself. I was looking for concat() method.
for (var i = 0; i < peoples.length / 2; i++) {
array1[i] = array1[i].concat(array2[i])
}
https://jsfiddle.net/danny3b/rfju9949/
What join does is concatenating all of the strings in the array. Instead of that, you should insert all of the elements in there.

Compare original array with a secondary array and change value if matched with original array

I have an original array with "tags" (40 rows) with an objects with:
tag: String?
selected: Bool? (false)
I then make a new array with "tags" from the original array lets say with 5 rows. Where selected is true.
I now want to change selected = true in my original array with the matching tags.
This was my bet:
originalArray = originalArray.filter{ selectedTags.map({ $0.tag }).contains($0.tag) }.forEach({ $0.selected = true})
Cannot invoke 'forEach' with an argument list of type '(#noescape (TagViewModel) throws -> ())'
This is how I do it:
yourOriginalArray.forEach({
let tempItem = $0
if yourSelectedArray.contains( { return $0.tag == tempItem.tag } ) {
$0.selected = true
}
})
I like #Breek's answer better and he posted while I was still typing my answer up, but here it is anyway! You can do something like:
struct TagViewModel : Equatable {
var tag: String
var selected: Bool
init(tag: String, selected: Bool) {
self.tag = tag
self.selected = selected
}
}
func ==(lhs: TagViewModel, rhs: TagViewModel) -> Bool {
return lhs.tag == rhs.tag
}
var originalArray = [
TagViewModel(tag: "1", selected: false),
TagViewModel(tag: "2", selected: false),
TagViewModel(tag: "3", selected: false),
TagViewModel(tag: "4", selected: false),
TagViewModel(tag: "5", selected: false),
TagViewModel(tag: "6", selected: true),
TagViewModel(tag: "7", selected: false),
TagViewModel(tag: "8", selected: true),
TagViewModel(tag: "9", selected: false)
]
let newArray = [
TagViewModel(tag: "1", selected: true),
TagViewModel(tag: "2", selected: true),
TagViewModel(tag: "3", selected: true)
]
for tagViewModel in newArray {
if let index = originalArray.indexOf(tagViewModel) {
originalArray[index].selected = tagViewModel.selected
}
}
You could simply do something like:
originalArray = originalArray.map({ element in
let newArrayFiltered = newArray.filter({ $0.tag == element.tag })
return (newArrayFiltered.count > 0) ? (newArrayFiltered[0]) : (element)
})
Although I like #breek's response :)

Resources