2

At first let me explain what I want. Well, I have a sorted array like this

var arr = ["ab", "abcd", "abdf", "abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"];

Now I need to remove all elements which string length is less than 5. Ok, Now my point is, is there any easiest way to find the index of the element that string length is 4. Likes here two elements contain string length 4. But I need the last one. If it is possible to get that index I can apply

arr.splice(0, (index+1));

And one thing, my original array contain 1 million data. Now how can I solve this?

Tushar
  • 85,780
  • 21
  • 159
  • 179
  • You've got it. Loop through the array and splice out the entries you don't want. If you have a javascript array with 1,000,000+ objects, you probably have a deeper issue. – CollinD Sep 26 '15 at 06:07
  • can you just make a new filtered array? or does it have to mutate the orig? `arr2=arr.filter(/./.test, /[\w\W]{5}/)` – dandavis Sep 26 '15 at 06:10
  • Thank @CollinD. But I really need that. Cause on next they array contains 1 core data. So I need an efficient way to do that. – Sudarshan Biswas Sep 26 '15 at 06:12
  • While `filter()` is the simplest way, it'll be more efficient to use a simple for loop to find the index of the last element with length 4 (or whatever) and do one slice using that. Especially if the number of elements > length 4 is large. – techfoobar Sep 26 '15 at 06:12
  • @SudarshanBiswas Even filtering the array will still have to iterate over the entire array. That's unavoidable. You could potentially save time by constructing a new array of elements that are longer than length 4, but it really depends on your data. – CollinD Sep 26 '15 at 06:13
  • @dandavis - The solution i suggested does just a single splice. – techfoobar Sep 26 '15 at 06:15
  • IF they are sorted by length already: `arr2=arr.slice(arr.findIndex(/./.test, /[\w\W]{5}/))` – dandavis Sep 26 '15 at 06:18
  • @techfoobar: oh, i didn't realize the sort was by length not abc; you're right – dandavis Sep 26 '15 at 06:19
  • @dandavis - I misread the question (thought he needs to remove all with length > 4 !!!). Still the solution is pretty valid (and efficient). See my answer below (updated after your latest comment). – techfoobar Sep 26 '15 at 06:20
  • @CollinD You can avoid iterating over array if you use regex, see my answer below – Tushar Sep 26 '15 at 06:40
  • @SudarshanBiswas Can you please provide a sample array which is having 10k+ elements, it'll be easier to compare the efficiency of all the different approaches – Tushar Sep 26 '15 at 06:42
  • @Tushar you really can't. Your solution may be more efficient due to optimizations in the built-in functions but internally both array.join and regex iterate over the entire array. That said, array.join() is notoriously slow and I'd be curious how it performs. Creative solution though – CollinD Sep 26 '15 at 06:52
  • @CollinD Yes, I'm waiting for sample array having atleast 10k elements so that I can see how it performs as compared to iteration solutions – Tushar Sep 26 '15 at 06:56
  • http://jsperf.com/removing-elements-till-last-index/12 – techfoobar Sep 26 '15 at 07:38
  • @Tushar here is sample data. http://pastebin.com/hJKYQEUn – Sudarshan Biswas Sep 26 '15 at 20:27

7 Answers7

3

You can use array filter to remove elements.

var arr = ["ab", "abcd", "abdf", "abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"];
arr = arr.filter(function(item) { 
  return item.length > 4;
});
//["abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"]

I don't know how you have one million items in the array, but maybe you could try to reduce that array in the server before sending it to the client. (I don't know exactly where your data comes from, but that is a lot of data for a javascript array);

Marcos Casagrande
  • 37,983
  • 8
  • 84
  • 98
  • when you want to search an element with maximum length, i.e. when the element will be at the end of the array, this solution will be much slower – Tushar Sep 26 '15 at 06:54
  • 1
    Some interesting performance info: http://jsperf.com/provs-sample/4 Surprisingly the naive approach seems significantly faster. – CollinD Sep 26 '15 at 07:21
2

While filter() is the simplest way, it'll be more efficient to use a simple for loop to find the index of the last element with length 4 (or whatever) and do a single slice() to get the result. Especially if the number of elements > length 4 is large.

var arr = ["ab", "abcd", "abdf", "abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"],
    lastIndex = false;

// find the number of elements to be removed
for ( var i = 0; i < arr.length; i++ ) {
    if ( arr[i].length >= 5 ) {
        lastIndex = i;
        break; // break out of the loop
    }
}

// one single splice to get the result
arr.splice(0, lastIndex); 

// arr is now ["abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"]

And here is a performance comparison of filter and loop & splice (above) strategies. As you can see the above method is leaps and bounds ahead of filter.

techfoobar
  • 65,616
  • 14
  • 114
  • 135
  • The performance comparison you've added just have a fewer elements ~10, it should have atleast 10k+ elements – Tushar Sep 26 '15 at 06:45
  • Another thing is that when you want to search an element with maximum length, i.e. when the element will be at the end of the array, this solution will be much slower, so your `performance test` does not prove anything at this time – Tushar Sep 26 '15 at 06:47
  • @Tushar - Here is a test with a few thousand elements ranging from length:2 to length:11 sorted by length. Like you said when the number of elements to be removed is very large (i.e. matching elements very near or at the end of the array) - the native filter is faster. But in **all** other cases, the loop & slice is way faster as it does not involve looping through the whole array. – techfoobar Sep 26 '15 at 07:37
2

Good thing is that you have the array sorted on the length, bad new is that you have array of 100,000+ elements.

Regex on string is faster than the looping over array(specially in case of 100k+ elements)

You can use regex to get the last elements with a specified length and then you can use splice on the array using the index.

  1. Convert the array to string using join
  2. Use regex to extract all the strings whose length is n
  3. Get the last element from the matched set
  4. Get the last index of this element from the original array
  5. splice original array using this index

Demo

var arr = ["ab", "abcd", "abdf", "abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"];

var matches = arr.join(",").match(/\b[a-z]{5}\b/ig) || [];

arr.splice(0, arr.lastIndexOf(matches[matches.length - 1]) + 1);

console.log(arr);
document.write(arr);

Regex Explanation

  1. \b: Word Boundary
  2. [a-z]: Matches all alphabets
  3. {n}: Matches previous class exactly n times
  4. i: Incase-sensitive match
  5. g: Global, get all matches

Binary Search

You can also use Binary search to split your array in two equal sub-arrays and search in individual sub-array for the last element having the length specified. And then use this element to get the index of it and then splice the original array.

Search algorithm courtesy of Binary Search in Javascript

var arr = ["ab", "abcd", "abdf", "abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"];

var copy = arr.slice();

function binarySearch(arr, len) {
  var mid = Math.floor(arr.length / 2);
  console.log(arr[mid], len);

  if (arr[mid].length === len && arr[mid + 1].length === len) {
    console.log('match', arr[mid], len);
    return binarySearch(arr.splice(mid));
  } else if (arr[mid].length === len) {
    return arr[mid];
  } else if (arr[mid].length < len && arr.length > 1) {
    console.log('mid lower', arr[mid], len);
    binarySearch(arr.splice(mid, Number.MAX_VALUE), len);
  } else if (arr[mid].length > len && arr.length > 1) {
    console.log('mid higher', arr[mid], len);
    binarySearch(arr.splice(0, mid), len);
  } else {
    console.log('not here', len);
    return -1;
  }
}

var result = binarySearch(copy, 5);

arr.splice(0, arr.lastIndexOf(result) + 1);

console.log(arr);
document.write(arr);
Community
  • 1
  • 1
Tushar
  • 85,780
  • 21
  • 159
  • 179
  • Heads-up: this code will error if there are no elements with exactly length 5. Further it is incorrect output for the test case `arr = ["abcde","ab","a","abf"]` Unless I'm understanding the question incorrectly. – CollinD Sep 26 '15 at 07:16
  • @CollinD Thanks for pointing out the mistake, prevented error – Tushar Sep 26 '15 at 07:19
  • Disregard my comment on the incorrect output. I missed the portion where it was specified that the array was sorted by length already. – CollinD Sep 26 '15 at 07:21
1

Try something like this

  1. Remove all elements that length less then 5

       var arrFiltered = arr.filter(function(element){ return element.length>=5});
    
  2. Get index of last element with length 4

       var lastId = 0;
       var filteredElements = arr.filter(function(e){ 
                  return e.length === 4;
       };
    
       lastId = filteredElement[filteredElement.length-1]?arr.indexOf(filteredElement.pop());
    
Oleh Dokuka
  • 11,613
  • 5
  • 40
  • 65
  • when you want to search an element with maximum length, i.e. when the element will be at the end of the array, this solution will be much slower – Tushar Sep 26 '15 at 06:54
1

Try using do.. while loop

do {
  arr.splice(0,1)
} while (arr[0].length < 5)
guest271314
  • 1
  • 15
  • 104
  • 177
  • when you want to search an element with maximum length, i.e. when the element will be at the end of the array, this solution will be much slower – Tushar Sep 26 '15 at 06:52
1

following Tushar's comment, if the array is sorted, there is a more efficient lodash approach:

arr = _.takeWhile(arr, function(element){ return element.length < 5; });

--- old appaorch ---

You can use lodash to do this nice, clean and simple:

arr = _.filter(arr, function(element){return element.length < 5; };

var arr = ["ab", "abcd", "abdf", "abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"];

// arr = _.filter(arr, function(element){return element.length < 5; });
arr = _.takeWhile(arr, function(element){ return element.length < 5; });

alert('filtered array: ' + arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
Meir
  • 14,081
  • 4
  • 39
  • 47
  • First thing `lodash` is not tagged, OP didn't mention a solution using loadash. Second thing when you want to search an element with maximum length, i.e. when the element will be at the end of the array, this solution will be much slower – Tushar Sep 26 '15 at 06:53
  • You are correct about the efficiency, I will post a modified solution, as for lodash, not sure I understand the relevancy of not being mentioned. – Meir Sep 26 '15 at 06:56
1

We can also use .grep function of jquery like bellow

var arr = ["ab", "abcd", "abdf", "abcdd", "abcdd", "abcdfd", "abcdsss", "abcdefgh", "abcdsdsdsds"],

            arr=$.grep(arr,function (obj,i){
                return obj.length > 4
            })
            console.log('value is'+arr);

It also generally used to filter from a array

Chandan Sarma
  • 308
  • 2
  • 5
  • 19
  • when you want to search an element with maximum length, i.e. when the element will be at the end of the array, this solution will be much slower – Tushar Sep 26 '15 at 06:53