Javascript sort array of objects by a boolean property - javascript
See edit at end for actual problem.
Ok, I have this scenario:
a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
Then if I do this:
a.sort(function(a,b){return !a && b});
It gives me this:
[false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
It's sorta doing a sort... but not quite... :(
How do I sort this array?
EDIT:
If you are wondering why I did not use just a.sort() is because my actual array is of objects, not a plain array like the one I posted. The real one has elements that look like [{xx:true},{xx:false},...]
a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
a.sort(function(x, y) {
// true values first
return (x === y)? 0 : x? -1 : 1;
// false values first
// return (x === y)? 0 : x? 1 : -1;
});
console.log(a);
You must return 0 when a and b both have the same value, -1 if a is true and 1 otherwise.
To prevent implicit type conversion (which languages like TypeScript don't like), you can use Number() to explicitly convert the boolean to a number:
a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
a.sort(function(x, y) {
return Number(x) - Number(y);
});
console.log(a);
Or using arrow functions:
a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
a.sort((x, y) => Number(x) - Number(y));
console.log(a);
a simpler way:
a = [{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false}];
a.sort(function(a,b){return a.xx-b.xx});
console.log(a);
you can call a.reverse() after the sort() if you want it sorted the other way..
EDIT:
edited to reflect updated question of sorting an array of objects instead of an array of booleans.
An array does not have any equal positions, so why not leave away the equals check, and always return either -1 or 1. This approach works well with TS.
a.sort(x => x ? -1 : 1)
Note: I am a bit concerned how this affects internals of the sort function, but it seems to do the trick.
If you want to reverse sort
a.sort(x => !x ? -1 : 1)
Simple solution:
[true, false, true, false].sort((a, b) => b - a)
console.log([true, false, true, false].sort((a, b) => b - a));
PFB the solution worked for me in Typescript Angular 2 as well,
let a = [{aa:"1",xx:true},{aa:"10",xx:false},{aa:"2",xx:true},{aa:"11",xx:false},{aa:"3",xx:true},{aa:"12",xx:false},{aa:"4",xx:true},{aa:"13",xx:false},{aa:"5",xx:true},{aa:"14",xx:false},{aa:"6",xx:true},{aa:"15",xx:false},{aa:"7",xx:true},{aa:"16",xx:false},{aa:"8",xx:true},{aa:"17",xx:false},{aa:"9",xx:true},{aa:"18",xx:false}];
//a.sort(function(a,b){return a.xx-b.xx});
a.sort(function (x, y) {
// true values first
return (x.xx === y.xx) ? 0 : x ? -1 : 1;
// false values first
// return (x === y)? 0 : x? 1 : -1;
});
return JSON.stringify(a);
I also ran into this issue, here is my part, I hope it helps:
orders.sort((x, y) => {
if (x === y) return 0;
if (x) return -1;
return 1;
});
I wanted to see if I could do it without using the ? : operator, just for fun.
Note
This works on all sortable data types (strings, numbers), not just raw booleans. I'm not sure if this is faster than ? : and it's more convoluted. I just get sick of conditionals so it's just personal preference.
var b = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
.sort((a,b) => Number(a > b) * 2 - 1);
I can turn that into a utility function and give it a meaningful name:
var sortOrder = {
asc: (a,b) => Number(a > b) * 2 - 1,
desc: (a,b) => Number(a < b) * 2 - 1
}
so then I can:
var b = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
.sort(sortOrder.asc);
A pretty simple solution for the comparison function is to check if a < b, this gives 0 or 1 when converted to a number. We then want to map 0 to -1 and 1 to 1. To do that you can just multiply by 2 then subtract 1.
data.sort(function (a, b) {
return (a < b) * 2 - 1
}
or just
data.sort((a, b) => (a < b) * 2 - 1)
Problem sorted!
If any of your values are null they are treated as false (null*2 === 0) and any value that is undefined will become NaN (undefined*2 === NaN) which should make it last in either sort direction.
a=[true,false,true,false,true];
a.sort(function(x, y) {
a1=x?1:0
b1=y?1:0
return a1-b1
});
A boolean array with enough entries to represent all transitions i.e. true to true, true to false, false to false, false to true.
var boolarray = [true, false, true, true, false, false, true]
boolarray.sort( (a,b) => !(a ^ b) ? 0 : a ? -1 : 1)
The sort inverts the xor of the inputs. If the inputs are the same then return 0, if they are not then, if the 'a' input is true 'b' must be false so return -1, and vice versa return 1.
'a' and 'b' booleans are sorted when they differ and are ignored when
the same.
To use this method with objects just use the object member names for the sort arguments:
var objarray = [{xx:true}, {xx:false}, {xx:true}, {xx:true}, {xx:false}, {xx:false}, {xx:true}]
objarray.sort( (a,b) => !(a.xx ^ b.xx) ? 0 : a.xx ? -1 : 1)
You can actually just do like the following:
const sortedData = data.sort(
(a, b) => Number(b) - Number(a),
);
This solution is ok with Typescript has well since the conversion of boolean to number is explicit.
I got typescript errors on the return (x.xx === y.xx) ? 0 : x ? -1 : 1;
This is my solution when you want to sort on a boolean property
this.mediaList.sort( (a: MediaAutosubscriptionModel, b: MediaAutosubscriptionModel) => {
let status1: number = a.status === StatusEnum.ACTIVE ? 1 : 0;
let status2: number = b.status === StatusEnum.ACTIVE ? 1 : 0;
let comparison: number = 0;
let direction: number = this.sortDirection === SortDirectionsEnum.ASC ? -1 : 1;
if (status1 > status2) {
comparison = direction;
} else if (status1 < status2) {
comparison = -1 * direction;
}
return comparison;
});
Without using any ES6 function with time and space optimization -
const data = [false, true, true, true, false, false, false, true];
let lastElementUnchecked;
for(let i=0; i<data.length; i++){
if(data[i] && lastElementUnchecked !== undefined){
let temp = data[i];
data[i] = data[lastElementUnchecked];
data[lastElementUnchecked] = temp;
i = lastElementUnchecked;
lastElementUnchecked = undefined;
}else{
if(!data[i] && lastElementUnchecked === undefined){
lastElementUnchecked = i;
}
}
}
console.log(data)
Related
Javascript - compare boolean arrays and creating a new one
I'm curious how I could compare these array's, bare in mind this is an example, it's not limited to 4 array's, as this could be more or less depending on user selection) let array0 = [false,true,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,true ,true ,false,false,false,false,false,false,false,false,true ,true ,true ,true,true,false,true,true,true,true,false,false] let array1 = [true ,true,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true ,true ,true ,true ,false,true ,false,true ,true ,false,true ,true ,true ,true ,true,true,true ,true,true,true,true,true ,true ] let array2 = [true ,true,true ,true ,true ,true ,true ,true ,true,true ,true ,true ,true ,true ,true ,true ,true ,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true ,true,true,true,true,true ,true ] let array3 = [true ,true,true ,true ,true ,true ,true ,true ,true,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true,true,true ,true,true,true,true,true ,true ] The end goal is to get a new array with a true or false value based on comparison/validation of all of the above array's, a true value should only be set if all 4 array's have a true value in that index position, and a false if of them or not for example: Array result Array result Array result array0[0] false array0[1] true array0[2] false array1[0] true array1[1] true array1[2] false array2[0] true array2[1] true array2[2] true array3[0] true array3[1] true array3[2] false result false true false let resultArray = [false, true, false, false, /* etc */] Hope I have explained this correctly.
You could transpose the array and map all items having truthy values. const array0 = [false, true, false, true, true], array1 = [true, true, false, false, true], array2 = [false, true, true, false, true], array3 = [true, true, true, true, true], transpose = (r, a) => a.map((v, i) => [...(r[i] || []), v]), result = [array0, array1, array2, array3] .reduce(transpose, []) .map(a => a.every(Boolean)) console.log(result);
I think you should create an array containing all your arrays. Then you will probably need a double for loop to calculate individual values. Check the nth index for every array and compute the value, where i is from 0 to the length of array (Basically, do the check for all the indices). You should get your computed result. Below is using only 2 arrays: let array0 = [false,true,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,false,true,true,true,true,false,false] let array1 = [true,true,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,true,true,true,true,false,true,false,true,true,false,true,true,true,true,true,true,true,true,true,true,true,true,true] let newArr = [array0,array1]; let ans = []; for(let i = 0 ; i < array1.length; i++){ let val = true; for(let j = 0 ;j < newArr.length; j++){ val = val && newArr[j][i]; if(!val) break; } ans.push(val); } console.log(ans);
let array0 = [false, true, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, true, true, true, true, true, false, true, true, true, true, false, false] let array1 = [true, true, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, true, true, true, false, true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true] let array2 = [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true] let array3 = [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true] let MAX_INDEX = Math.max(array0.length, array1.length, array2.length, array3.length) let result = [] for (let i = 0; i < MAX_INDEX; i++) { result[i] = array0[i] || false && array1[i] || false && array2[i] || false && array3[i] || false } console.log(result)
Since all your arrays have same length I guess you could simply do that: let array0 = [false, true, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, true, true, true, true, true, false, true, true, true, true, false, false] let array1 = [true, true, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, true, true, true, true, false, true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true] let array2 = [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true] let array3 = [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true] let result = Array.from({ length: array0.length }).map((_, i) => array0[i] && array1[i] && array2[i] && array3[i]) console.log(result)
Regex validation to match urls
I have this regex /[-a-zA-Z0-9#:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}([a-zA-Z0-9#%._~#?={}]+-*[a-zA-Z0-9#%._~#=\/;]+)+.(?!-)[a-z]{2,24}\b((?:\{[^{}]*}|[-a-zA-Z0-9#%_+.~?&#\/=:$\[\]])*)$/ Which I want to match with these urls to true since I'm getting false with my current regex: "ftp://user#host.com:80/abc/": true "ftp://host.com": true "http://abcewe.com": true "https://abcewe.com": true "mailto:someone#example.com": true "news:newsgroup-name": true "telnet://user:password#host:100": true "http://ab-c.com": true What do I need to add to match these urls? Thank you very much for the help. Note: I need the other codes so I cant remove them. Note: Here is the full urls that I need to match. The rest aside above is already match with this regex, the above urls isnt. { "http://goog": false, "http://googl": false, "http://google": false, "https://www.domains.com/#home": true, "https://www.domains.com/{firstname}_{lastname}": true, "https://www.domains.com/file[3].html": true, "https://www.domains.com/?name=fred;age=50": true, "https://www.domains.com/{name{age}}": false, "https://www.domains.com/?name=fred;": false, "http://www.google.com": true, "https://www.amazon.com": true, "http://joe:secret#www.liquidcrystalstudios.co.cc:8080/abc/": true, "http://www.abcde.com:80/abc/": true, "ftp://user#host.com:80/abc/": true, "http://www.abcde.com/abc/": true, "http://www.abcde.com/": true, "http://joe:secret#www.liquidcrystalstudios.co.cc": true, "http://jocce:seccccret#www.liquidcrystalstudios.co.cc": true, "ftp://:abc#host.com/": false, "ftp://foo:#host.com/": false, "ftp://host.com": true, "http://abcde.com": true, "https://abcde.com": true, "gopher://gopher.meulie.net/": true, "mailto:someone#example.com": true, "news:newsgroup-name": true, "nntp://wild.server.example/example.group.n%2Fa/12345": true, "telnet://user:password#host:100": true, "wais://100.64.0.0:5": true, "file://vms.host.edu/disk$user/my/notes/note12345.txt": true, "prospero://host.dom//pros/name": true, "abc://a.com": false, "://abc.com": false, "http//abc.com": false, "http:abc.com": false, "HTTP://www.ABCDW.com/ABC/": true, "http://www.亜蔭.com/亜蔭/": false, "http://www.弌傲僉.com/弌傲僉": false, "http://www.俱咡.com/俱咡": false, "http://www.丂㒒儈.com/丂㒒儈": false, "http://www.亜腕弌.com/亜腕弌": false, "http://www.アバ.com/ショアバ": false, "http://www.アバショ.com/ジウンカ": false, "http://174.129.0.77": true, "http://174.1a.a.77": false, "http://174.10.23.0": false, "http://0.10.23.7": false, "http://174.10.23.+7": false, "http://-174.10.23.7": false, "http://17-4.10.23.5": false, "http://1.0.0.1": true, "http://126.255.255.254": true, "http://100.25.36.14": true, "http://128.0.0.1": true, "http://191.255.255.254": true, "http://150.25.36.14": true, "http://192.0.0.1": true, "http://223.255.255.254": true, "http://200.25.36.14": true, "http://10.0.0.0": false, "http://10.1.2.3": true, "http://10.255.255.255": true, "http://172.16.0.0": false, "http://172.0.0.5": true, "http://172.31.255.255": true, "http://192.168.0.0": false, "http://192.168.4.4": true, "http://192.168.255.255": true, "http://126.255.255.255": true, "http://127.0.36.14": true, "http://127.112.0.1": true, "http://10.190.201.10:0?port=0": false, "http://10.190.201.10:1?port=1": true, "http://10.190.201.10:21?port=21": true, "http://10.190.201.10:80?port=80": true, "https://10.190.201.10:443?port=443": true, "http://10.190.201.10:65535?port=65535": true, "http://10.190.201.10:65535?port=65536": false, "http://gooo.gl/09GxbZc": true, "www.abc.com/miss-scheme": false, "http&://10.190.201.10/wrong-scheme": false, "http://test#url/wrong-hostname": false, "http://FE80:0000:0000:0000:0202:B3FF:FE1E:8329/hostname-ipv6": false, "http://10.190.201.10:10/path/1": false, "HTTP://www.ABC①㌔№∮.com/①㌔№∮C/": false, " http://🙂 😁 😆": false, " http://zzz": false, " :// . . ": false, "https :// www. amazon. com": false, "ht tps://ww w.am azon.co m": false, "https://www.amazon.com ": false, "https:// www. amazon. com ": false, "htt ps://ww w.ama zon.c om ": false, "https ://www .amazon .com ": false, " https:// www. amazon. com": false, "http://-abc.com": false, "http://abc-.com": false, "http://ab-c.com": true, "http://.abc.com": false, "http://abc..com": false, "http://ab.c.com": true, "http://+abc.com": false, "http://ab+c.com": false, "http://abc+.com": false, "http://www.356.com": true, "http://www.35accd6.com": true, " http:/abc.com/acb": false, "http:/abc.com/acb ": false, "http:/a bc.com/a cb": false, "http://a.c": false, "http://.com": false, "http://www.com": false }
get property from UserAgent object (koa-useragent)
UserAgent { _agent: { isYaBrowser: false, isAuthoritative: true, isMobile: true, isMobileNative: false, isTablet: false, isiPad: false, isiPod: false, isiPhone: false, isiPhoneNative: false, isAndroid: true, isAndroidNative: false, isBlackberry: false, isOpera: false, isIE: false, isEdge: false, isIECompatibilityMode: false, isSafari: false, isFirefox: false, isWebkit: false, isChrome: true, isKonqueror: false, isOmniWeb: false, isSeaMonkey: false, isFlock: false, isAmaya: false, isPhantomJS: false, isEpiphany: false, isDesktop: false, isWindows: false, isLinux: true, isLinux64: false, isMac: false, isChromeOS: false, isBada: false, isSamsung: false, isRaspberry: false, isBot: false, isCurl: false, isAndroidTablet: false, isWinJs: false, isKindleFire: false, isSilk: false, isCaptive: false, isSmartTV: false, isUC: false, isFacebook: false, isAlamoFire: false, isElectron: false, silkAccelerated: false, browser: 'Chrome', os: 'Linux', platform: 'Android', geoIp: {}, isWechat: false, electronVersion: '' } } I use this package for getting user agent https://www.npmjs.com/package/koa-useragent . Here how I get object you see above util.inspect(ctx.userAgent) , but it is string. I've tried to make JSON.parse(util.inspect(ctx.userAgent)) , but it doesn't work this way. How can I parse this string or convert it to object , to get properties inside of _agent ?
ctx.userAgent._agent.isMobile - you don't need call inspect object
how to check first 3 or 4 elements are true in nodeJS?
time : [ true, true, true, true, true, true, true, false, false ] this is an array I'm getting, I want to find with if condition whether first for elements are true or not?
Just slice and check then. (you can use Array.prototype.every for the checking) const time = [ true, true, true, true, true, true, true, false, false ] console.log(time.slice(0, 4).every(item => item))
This is a really simple task using the slice and includes method. First slice the list, then check to see if it includes any false values. let firstFourAreTrue = !time .slice(0, 4) .includes(false);
In the description of the question time I think it is an object. This code returns values for 3 and 4. The values are set in an array and other values can be added. var t = { time: [ true, true, true, true, true, true, true, false, false ] }; var p = [3, 4]; p.forEach(i => { var ret = t.time.slice(0, i).every(x => x); console.log('First',i, ret); });
A solution without slicing. const check = array => array.every((v, i) => i >= 4 || v); console.log(check([true, true, true, true, true, true, true, false, false])); console.log(check([false, true, true, true, true, true, true, false, false])); console.log(check([true, false, true, true, true, true, true, false, false])); console.log(check([true, true, false, true, true, true, true, false, false])); console.log(check([true, true, true, false, true, true, true, false, false]));
Loop through array and draw * when value is true
I have an array with values true and false. I want to check if a value of an array is true draw '*'. And each row should move 1 position left. Expected result -> '***** ***** ***** ***** *****' I tried this way. let points = [ [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false] ]; let m = 10; let n = 5; let board = ""; let space = '\xa0'; for(let i = 0; i < points.length; ++i) { for(let j = 0; j < points[i].length; ++j) { if(points[i][j]) { for (let y = 0; y < points[i]; y++) { if(y > 0) { board += space + "*"; space += space; } for (let x = 0; x < points[i][j]; x++) { board += "*"; } board += "\n"; } } return ''; } }
You can use a couple reduce() functions to print the outer and inner loop using the index to control the initial spaces. The outer reduce iterates over each row and the inner reduce makes the * strings based on the boolean values. let points = [ [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false] ]; let result = points.reduce((str, arr, i) => { return str + ' '.repeat(i) + arr.reduce((subst, bool) => subst + (bool ? '*' : ''), '') + '\n' }, '') console.log(result)
points .map((row, i) => " ".repeat(i) + row.map(cell => (cell ? "*" : " ")).join("")) .join("\n");
Based on your description, and if you want to stick to loops, the loop should be similar to this. let points = [ [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false] ]; let board = ""; for (let i = 0; i < points.length; i++) { for (let j = 0; j < points[i].length; j++) { if (j === 0 && i !== 0) { board += "<br>"; for (let k = 0; k < i; k++) { board += "\xa0"; } } board += points[i][j] ? "*" : " "; } } document.write(board); However if you want the "expected result" that you showed, you need to remove the adding of spaces. Your expected result doesn't match the description in this regard.
I would just use Array.prototype.map. The code here is using contemporary ES6 for brevity but could easily be ported to ES5 if need be. let points = [ [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false], [false, true, false, true, false, true, false, true, false, true], [true, false, true, false, true, false, true, false, true, false] ]; points.map((row, i) => { const padded = []; while (i--) { padded.push(' '); } return [ ...padded, ...row.map(point => point ? '*' : ' ') ].join(''); }).join('\n'); I'm using: the spread operator to spread the padded and points array items into a new array which is then just joined into a string arrow functions () => {} and an implicit return statement as the callback functions to map Outputs