-1

I am a javascript beginner trying to solve a challenge posed by an online code camp. This problem is similar to that posed at Using an array to filter an array Javascript. I have studied this but remain unsuccessful. I have tried for many hours without arriving at a solution.

The exact challenge states:

Remove all values (last argument(s)) from an array (first argument) and >return as a new array.

I know this is not much to go on. In the example below, the desired output would be the following array:

[1,1]

The basic premise is to filter the first argument, which is an array, by x number of non-array arguments. Where the array elements are the same as the non-array arguments, those elements would be removed from the array. Below is a non-functioning sample of a path I've attempted:

<code>
     function result(arr) {
  var list = [];
  var i;
  for(i in Object.keys(arguments)) {
      if(i > 0) {
          list.push(arguments[i]);
      }
  }

  function answer(arr1, arr2) {
    return arr1.filter(function(a) {
        return arr2.indexOf(a) >= 0;
    });
  }

  return answer(arr, list);
}
result([1, 2, 3, 1, 2, 3], 2, 3); 
</code>
Community
  • 1
  • 1
davisec52
  • 357
  • 1
  • 2
  • 8
  • If you seriously want help with a coding challenge, then please post the EXACT instructions for the challenge including any rules or limitations a solution must live by and exact samples for desired inputs and outputs. – jfriend00 Mar 01 '15 at 20:34
  • Sorry, I am new to this. The complete and exact instruction state the following: "Remove all values (last argument(s)) from an array (first argument) and return as a new array." – davisec52 Mar 01 '15 at 20:43
  • 1
    Sorry, but that description is not clear to me. If this is a specific challenge, then please paste (into your question), the EXACT challenge. Removing all values from an array and retrurning as an array doesn't net-net change anything. If you show one or more inputs with the desired output, that would make it a lot clearer what you're trying to do. – jfriend00 Mar 01 '15 at 22:08
  • I've tried to add all the information the challenge made available as well as the desired outcome. The original instructions are not expansive. – davisec52 Mar 01 '15 at 22:46
  • `for(i in Object.keys(arguments)` - [Ouch!](http://stackoverflow.com/q/500504/1048572)! – Bergi Mar 01 '15 at 23:08

3 Answers3

0

OK, now maybe I understand. If that's the actual challenge wording, it is poorly worded.

My interpretation is that you are being asked to write a function that takes a first argument that is an array and then after that zero or more successive arguments. The task is to remove all the zero or more successive arguments from the array that was passed in and return a new array with those items removed.

function filterArray(sourceArray, arg1, arg2 /* ... more args */) {
    // get arguments after the first argument into their own array
    var args = Array.prototype.slice.call(arguments, 1);
    return sourceArray.filter(function(element, index, array) {
        return args.indexOf(element) === -1;
    });
}

This works by getting the successive arguments into their own array and then calling .filter() on the sourceArray. The callback for .filter() then uses .indexOf() to check if each element is in the argument list. If it is not, return true to keep the value in the output array. If it is in the argument list, return false so it is removed from the output array.

Working demo: http://jsfiddle.net/jfriend00/u8c5vvd9/


If you want compatibility with versions of IE before IE9, you can either add a polyfill for .filter() and .indexOf() which is easy and shown here and here or you could do the filtering manually like this:

function filterArray(sourceArray, arg1, arg2 /* ... more args */) {
    var result = [], found, item;
    for (var i = 0; i < sourceArray.length; i++) {
        item = sourceArray[i];
        found = false;
        for (var argIndex = 1; argIndex < arguments.length; argIndex++) {
            if (arguments[argIndex] === item) {
                found = true;
                break;
            }
        }
        if (!found) {
            result.push(item);
        }
    }
    return result;
}

Working demo: http://jsfiddle.net/jfriend00/g586cbpp/

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Many thanks for coming back to answer. Your answer, as well as David Thomas's above, will help me get a better grasp of javascript. Thank you, too, for the jsfiddle demo. – davisec52 Mar 01 '15 at 23:17
  • @davisec52 - I added a second version of the code that will work with versions of IE earlier than IE9. – jfriend00 Mar 01 '15 at 23:23
  • Thank you. I spent a long time working with for-loops but couldn't quite get them to work out correctly. Thank you for showing me how to properly approach this. – davisec52 Mar 01 '15 at 23:52
  • @davisec52 - since it looks like you might be new StackOverflow, are you aware that when you get one or more answers that answer your question, you can select a "best answer" by checking the green checkmark to the left of the answer. This will both indicate to the community that your question is now answered and will earn you and the person who provided the answer some reputation points. – jfriend00 Mar 02 '15 at 00:34
  • You are right. Very new. I have selected a best answer.Thank you for your help. – davisec52 Mar 05 '15 at 00:25
0

The easiest solution I can imagine would be:

function result(haystack) {
    // converting the 'non-array' list of additional arguments
    // into an array:
    var needles = Array.prototype.slice.call(arguments, 1),
        // using Array.prototype.filter() to filter the 
        // supplied array ('haystack'):
        filtered = haystack.filter(function (el) {
            // the first argument ('el') is the current array-element

            // if the current array-element is *not* found in the
            // array of 'needles', we retain the element:
            return needles.indexOf(el) === -1;
        });
    return filtered;
}

console.log(result([1, 2, 3, 1, 2, 3], 2, 3)); // [1, 1]

This solution, particularly the use of Array.prototype.filter(), is problematic in IE 8, the MDN reference link (below) offers a shim for those older browsers.

function result(haystack) {
  var needles = Array.prototype.slice.call(arguments, 1),
    filtered = haystack.filter(function(el) {
      return needles.indexOf(el) === -1;
    });
  
  /* just to display the values: */
  document.getElementById('haystack').textContent = haystack.join(', ');
  document.getElementById('needles').textContent = needles.join(', ');
  document.getElementById('result').textContent = filtered.join(', ');
  
  return filtered;
}

console.log(result([1, 2, 3, 1, 2, 3], 2, 3));
ol, li {
  list-style-type: none;
}

li::before {
  content: attr(id) ': ';
  display: inline-block;
  width: 20%;
  text-transform: capitalize;
}

li:empty {
  display: none;
}
<ol>
  <li id="haystack"></li>
  <li id="needles"></li>
  <li id="result"></li>
  </ol>

References:

David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • The `.filter()` callback should return `true` or `false`, not `el`. If `el` itself is `false` that will mess you up. – jfriend00 Mar 01 '15 at 23:04
  • @jfriend00: thanks, answer updated! And I see that, while we're ten seconds apart we've answered similarly (which is reassuring, in a sense). But a bit of a surprise. – David Thomas Mar 01 '15 at 23:06
  • Not surprising the answers are similar. It is a `.filter()` problem by nature and manually looping through the arguments is not as easy as extracting to an array and using `.indexOf()`. I think we both just followed the most logical path given an awareness of the tools JS has built-in. If you're going to use a snippet, it would be nice of your snippet showed the results in the snippet window. – jfriend00 Mar 01 '15 at 23:11
  • Thank you very much! This is an incredibly helpful lesson! – davisec52 Mar 01 '15 at 23:14
  • you're doing `if (expr_that_gives_bool) return bool;`, you can simplify it to `return needles.indexOf(el) === -1;` – Paul S. Mar 01 '15 at 23:42
  • @PaulS.: indeed, that's a remnant of an earlier issue in which I used `return el`, and then overlooked. Edited.. – David Thomas Mar 01 '15 at 23:44
0

This will check for an exact match of all arguments against the values in the input array. And then return an array stripped of those values.

It does not use .filter() .slice() .call() or Object.keys() so it is fully compliant with ancient web-browser versions.

<script type="text/javascript">

function result(input_array) {
    //  Breakpoint if input is not an array... return the object inside an array for consistency of return type
    if( !(input_array instanceof Array) ) return [].push(input_array);

    //  Gather list of arguments to reject.
    var reject_list = [];
    for(var i in arguments)
        if(i > 0)
            reject_list.push(arguments[i]);

    //  check each element in the input array if it is a value to reject.
    var output_array = []
    for(var i = 0; i < input_array.length; i++){
        var value_is_ok = true
        for(var r = 0; r < reject_list.length; r++){
            if( input_array[i] == reject_list[r] ){
                value_is_ok = false
                break
            }//endif
        }//endfor

        if( value_is_ok )
            output_array.push(input_array[i]);

    }//endfor


    return output_array
}

var my_array = [1, 2, 3, 1, 2, 3]
console.log( result(my_array, 2, 3) )   //  result is [1,1]

</script>
FactoryAidan
  • 2,484
  • 1
  • 13
  • 13
  • You appear to want to claim ancient browser compatibility, yet you use `Object.keys()` which requires IE9 which is the same `.filter()` so you aren't buying yourself any extra compatibility. – jfriend00 Mar 01 '15 at 23:17