Sort a JavaScript array of strings with years inside - javascript

Evaluating 2 extremely simple arrays, one sorts ok and the other does not:
var qtr = ['zzqtr_1_2020','zzqtr_2_2020','zzqtr_3_2019','zzqtr_4_2019'];
qtr.sort();
// qtr is now zzqtr_1_2020, zzqtr_2_2020, zzqtr_3_2019, zzqtr_4_2019 (no change)
var fruits = ["a_1_Banana", "a_2_Orange", "a_1_Apple", "a_1_Mango"];
fruits.sort();
// fruits is now a_1_Apple, a_1_Banana, a_1_Mango, a_2_Orange (SORTED!)
What I cannot figure out is why the qtr array will not be sort properly?

Because the array is already sorted. Your difference is made by the id after "zzqtr_". And the elements are already sorted.

The array is already sorted. The string after "zzqtr_" are in increasing order (ie: from 1 to 4). So, if you did something like this:
var qtr = ['zzqtr_3_2020','zzqtr_1_2020','zzqtr_4_2019','zzqtr_1_2019'];
qtr.sort();
var x = qtr.toString();
now, you will see a change :)

Array.prototype.sort() is comparing UTF-16 sequences, so zzqtr_1, zzqtr_2, zzqtr_3, zzqtr_4 is already sorted according to that, the last bit of text (the year) is not relevant there.
I assume you want to sort by year, so in that case, you need to implement a custom sorting function:
const qtr = ['zzqtr_1_2020','zzqtr_2_2020','zzqtr_3_2019','zzqtr_4_2019'];
qtr.sort((a, b) => {
const aParts = a.split('_');
const bParts = b.split('_');
return (parseInt(aParts[2]) - parseInt(bParts[2])) || a.localeCompare(b);
});
console.log(qtr.join(', '));

Array.prototype.sort() works by comparing UTF-16 code points, which can lead to unexpected results sometimes.
Instead, consider using String.prototype.localeCompare() function, which compares two strings, based on the alphabets of the locale and not UTF-16 code points

Related

Why this includes() case return false?

var product_id = "14728";
console.log(product_id.includes("14782,12434,3143")); // this return false
Example.
I just want to check that my product_id is within those other articles ids string
Two reasons:
You have it backwards. You are asking if "14728" includes "14782,12434,3143" (which it doesn't, the latter is too long to be included in the former for a start), not the other way around
"14782,12434,3143" doesn't include "14728" anyway
Your approach is also broken. "14782,12434,3143" includes "314". Presumably you want to only match complete numbers in the comma-separated list. You need to use Array.prototype.includes, not String.prototype.includes.
const myData = ["14782", "12434", "3143"];
const mySearch = "14728"; // Not in the array
const myOtherSearch = "12434"; // In the array
console.log(myData.includes(mySearch));
console.log(myData.includes(myOtherSearch));

create one array after using map() twice

I may or may not get in 2 differently formatted bits of data.
They both need to be stripped of characters in different ways. Please excuse the variable names, I will make them better once I have this working.
const cut = flatten.map(obj => {
return obj.file.replace("0:/", "");
});
const removeDots = flatten.map(obj => {
return obj.file.replace("../../uploads/", "");
})
I then need to push the arrays into my mongo database.
let data;
for (const loop of cut) {
data = { name: loop };
product.images.push(data);
}
let moreData;
for (const looptwo of removeDots) {
moreData = {name: looptwo};
product.images.push(moreData);
}
I wanted to know if there is a way to either join them or do an if/else because the result of this is that if I have 2 records, it ends up duplicating and I get 4 records instead of 2. Also, 2 of the records are incorrectly formatted ie: the "0:/ is still present instead of being stripped away.
Ideally I would like have a check that if 0:/ is present, remove it, if ../../uploads/ is present or if both are present, remove both. And then create an array from that to push.
You can do your 2 replace on the same map :
const processed = flatten.map(obj => {
return obj.file.replace("0:/", "").replace("../../uploads/", "");
});
Since you know the possible patterns, you can create a regex and use it to replace any occurrences.
const regex = /(0:\/|(\.\.\/)+uploads\/)/g
const processed = flatten.map(obj => obj.file.replace(regex, ''));
You can verify here
Note, regex is a pattern based approach. So it has pros and cons.
Pro:
You can have any number of folder nesting. Using string ../../uploads/ will restrict you with 2 folder structure only.
You can achieve transformation in 1 operation and code looks clean.
Cons:
Regex can be hard to understand and can reduce readability of code a bit. (Opinionated)
If you have pattern like .../../uploads/bla, this will be parsed to .bla.
Since you ask also about a possible way of joining two arrays, I'll give you couple of solutions (with and w/o joining).
You can either chain .replace on the elements of the array, or you can concat the two arrays in your solution. So, either:
const filtered = flatten.map(obj => {
return obj.file.replace('0:/', '').replace('../../uploads/', '');
});
Or (joining the arrays):
// your two .map calls go here
const joinedArray = cut.concat(removeDots);

Javascript - delete array items by value LIKE [duplicate]

This question already has answers here:
remove all items in array that start with a particular string
(5 answers)
Closed 7 years ago.
I have this simple array
var array = ['x.89999', 'y.sisisis', 'x.585858'];
I want to remove all the items in the array starting by 'x.' so to return this array:
['y.sisisis']
How can i do this without having to loop or iterate all the entire array? (i know it's probably not possible so don't mind if not possible)
Is there some builtin / native code i can use?
Thanks
you may use array.filter()
var newArray = array.filter(function(item){
return item.indexOf('x.') !== 1;
});
there is no way to do this job without looping through the whole array.
The only case – array is sorted alphabetically. But sorting demands looping through too
Assuming that the items to be removed will always precede the other items alphabetically (ie x is before y), you can sort your array and then break the loop as soon as the non-x value has been found:
function filter(arr) {
arr.sort();
for (var i = 0, l = arr.length; i < l; i++) {
if (arr[i][0] !== 'x') {
break;
}
}
return arr.slice(i);
}
filter(arr); // logs: "Iterated to 5 of array length 9"
DEMO
Of course, as JazzCat mentions, this is no good if you only have one y value and it's right at the end of the array - you're still going to have to iterate over the whole array.
Alternate option is to use regular expression, something like this. Fix your regex according to your requirement.
var array = ['x.89999', 'y.sisisis', 'x.585858'];
var filtArr = array.filter(function(x){
return x.match("^[x].+$");
});
console.log(filtArr);
One different answer not using explicit iteration or Array.prototype.filter:
var array = ['x.89999', 'y.sisisis', 'x.585858'];
array = array.join("#").replace(/(#?x\.\w*)/g,"").replace(/^#/,"").split("#");
//["y.sisisis"]
Of course you will need to take care of the separator character.
The good point is that it works on old browsers (ie8) while Array.prototype.filter does not.

JavaScript - split, choose all after a given number

I have an dynamic array and I want to exclude the first part of the string, but I don't know how many objects there will be after the first part and I want to include them all in a new string.
string = "text.'''hi''','''who''' '''are''' '''you'''. I'm ken and you're barbie"
x = string.split("'''")[1]
Is there something I can do to include them all? like [1..?]
I have JQuery but don't think that would be necessary right?
shift:
Removes the first element from an array and returns that element. This method changes the length of the array.
Code:
x = theString.split("'''");
var firstElement = x.shift();
// Now x is the shifted array.
x[0];
You seem to want:
x = string.split("'''").slice(1);
This will return all elements of the array starting at index 1.

fastest way to detect if a value is in a group of values in Javascript

I have a group of strings in Javascript and I need to write a function that detects if another specific string belongs to this group or not.
What is the fastest way to achieve this? Is it alright to put the group of values into an array, and then write a function that searches through the array?
I think if I keep the values sorted and do a binary search, it should work fast enough. Or is there some other smart way of doing this, which can work faster?
Use a hash table, and do this:
// Initialise the set
mySet = {};
// Add to the set
mySet["some string value"] = true;
...
// Test if a value is in the set:
if (testValue in mySet) {
alert(testValue + " is in the set");
} else {
alert(testValue + " is not in the set");
}
You can use an object like so:
// prepare a mock-up object
setOfValues = {};
for (var i = 0; i < 100; i++)
setOfValues["example value " + i] = true;
// check for existence
if (setOfValues["example value 99"]); // true
if (setOfValues["example value 101"]); // undefined, essentially: false
This takes advantage of the fact that objects are implemented as associative arrays. How fast that is depends on your data and the JavaScript engine implementation, but you can do some performance testing easily to compare against other variants of doing it.
If a value can occur more than once in your set and the "how often" is important to you, you can also use an incrementing number in place of the boolean I used for my example.
A comment to the above mentioned hash solutions.
Actually the {} creates an object (also mentioned above) which can lead to some side-effects.
One of them is that your "hash" is already pre-populated with the default object methods.
So "toString" in setOfValues will be true (at least in Firefox).
You can prepend another character e.g. "." to your strings to work around this problem or use the Hash object provided by the "prototype" library.
Stumbled across this and realized the answers are out of date. In this day and age, you should not be implementing sets using hashtables except in corner cases. You should use sets.
For example:
> let set = new Set();
> set.add('red')
> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false
Refer to this SO post for more examples and discussion: Ways to create a Set in JavaScript?
A possible way, particularly efficient if the set is immutable, but is still usable with a variable set:
var haystack = "monday tuesday wednesday thursday friday saturday sunday";
var needle = "Friday";
if (haystack.indexOf(needle.toLowerCase()) >= 0) alert("Found!");
Of course, you might need to change the separator depending on the strings you have to put there...
A more robust variant can include bounds to ensure neither "day wed" nor "day" can match positively:
var haystack = "!monday!tuesday!wednesday!thursday!friday!saturday!sunday!";
var needle = "Friday";
if (haystack.indexOf('!' + needle.toLowerCase() + '!') >= 0) alert("Found!");
Might be not needed if the input is sure (eg. out of database, etc.).
I used that in a Greasemonkey script, with the advantage of using the haystack directly out of GM's storage.
Using a hash table might be a quicker option.
Whatever option you go for its definitely worth testing out its performance against the alternatives you consider.
Depends on how much values there are.
If there are a few values (less than 10 to 50), searching through the array may be ok. A hash table might be overkill.
If you have lots of values, a hash table is the best option. It requires less work than sorting the values and doing a binary search.
I know it is an old post. But to detect if a value is in a set of values we can manipulate through array indexOf() which searches and detects the present of the value
var myString="this is my large string set";
var myStr=myString.split(' ');
console.log('myStr contains "my" = '+ (myStr.indexOf('my')>=0));
console.log('myStr contains "your" = '+ (myStr.indexOf('your')>=0));
console.log('integer example : [1, 2, 5, 3] contains 5 = '+ ([1, 2, 5, 3].indexOf(5)>=0));
You can use ES6 includes.
var string = "The quick brown fox jumps over the lazy dog.",
substring = "lazy dog";
console.log(string.includes(substring));

Categories