-1

Hi! So I want to get the following result: enter image description here

and here's the js function that i wrote. The problem is that the output of my function is null.

function dataTypeArray(arr, type){
    for(i=0; i<=arr.length-1; i++)
    var string = [];
    var number = [];
    var boolean = [];
    var i;
    if(type = 'string' && typeof arr[i] == 'string'){
        string.push(arr[i]);
    } else {
        if(type = 'number' && typeof arr[i] == 'number'){
            number.push(arr[i]);
        } else {
            if(type = 'boolean' && typeof arr[i] == 'boolean'){
                boolean.push(arr[i]);
            } else { 
                return 'null';
            }}} 
        return string, number, boolean;}


var arr = ['hhg', 'fthth', 456, true];
console.log(dataTypeArray(arr, 'string'));
Terra
  • 1
  • 1
  • 1. This `type = 'number'` should be `type === 'number'`. `=` is assignment and `==` or `===` is comparison. 2. `return string, number, boolean;` You can return only 1 value from a function. 3. `for(i=0; i<=arr.length-1; i++)` Missing braces. So body will only be next line, i.e. `var string = [];` – Rajesh Oct 14 '22 at 06:26
  • 4. Also, declare variables outside `for`. 5. This `return 'null';` will break the loop so you do not need this – Rajesh Oct 14 '22 at 06:32
  • @Terra ... From all the provided answers / approaches are there any questions left? – Peter Seliger Oct 20 '22 at 14:35

3 Answers3

0

I'd suggest using Array.prototype.filter.

var arr = ['hhg', 'fthth', 456, true];
var strings = arr.filter(x => typeof x === 'string');
var numbers = arr.filter(x => typeof x === 'number');
var booleans = arr.filter(x => typeof x === 'boolean');
Resonious
  • 98
  • 5
0

Rajesh has already listed in their comment what is going wrong in your current code. I'd recommend you to move to modern JavaScript, the task becomes quite easy:

function getTypeMap(arr) {
  const typeMap = {};
  for (const val of arr) {
    const key = `${typeof val}s`;
    typeMap[key]?.push(val) ?? (typeMap[key] = [val]);
  }
  return typeMap;
}

const exampleArr = [0, 'a', 1, 'b', true, 2, false],
  {booleans, numbers, strings} = getTypeMap(exampleArr);

console.log(booleans, numbers, strings);

As said, you can't return multiple values from a function. Here we create an object with keys by types of the values in the array, and return the object. In the loop, the code checks whether a property named by the type of the value exists using optional chaining operator (s is added at the end of the type because you can't create variables named function and undefined). If the operator returns undefined we'll create a new array with the current value in the first index (see nullish coalescing operator), otherwise we just push a new value to the type array.

Properties from the returned object are then assigned to variables using destructuring assignment ({booleans, numbers, strings} = ...). This way you can get any array of the types, just pick those you need when destructuring the returned object to the variables, or store the returned object to a variable, and use its properties where needed.

If you need more accurate type than typeof operator can return, you can do something like at this jsFiddle.

Teemu
  • 22,918
  • 7
  • 53
  • 106
0

One possible generic approach could be based on reduce and this method's 2nd initialValue parameter which will be passed to (and continued being passed around by) the reducer function.

This reducer is implemented around the initially passed object which features both a result object where the final result gets aggregated and a detection property which is a fully customizable key value based configuration of type detection functions.

Thus one implements the reducer once and then configures its usage at will.

function detectAndCollectType({ detection, result }, type) {
  const isAnyMatch = Object

    .entries(detection)
    .some(([ key, isType ]) => {
      const doesMatch = isType(type);
      if (doesMatch) {

        (result[key] ??= []).push(type);
      }
      return doesMatch;
    });

  if (!isAnyMatch) {
    (result.unhandled ??= []).push(type);
  }
  return { detection, result };
}

// 1st usage example.
const { result } = [null, void 0, 0, 'a', 1, 'b', true, 2, false, new Date]
  .reduce(detectAndCollectType, {
    detection: {
      boolean: val => 'boolean' === typeof val,
      number: val => 'number' === typeof val,
      string: val => 'string' === typeof val,
    },
    result: {},
  });

console.log({ result });

// 2nd usage example.
const { result: {

  nullish,
  boolean,
  number,
  string,
  date,
  
}} = [null, void 0, 0, 'a', 1, 'b', true, 2, false, new Date]

  .reduce(detectAndCollectType, {
    detection: {
      nullish: val => (val ?? null) === null,
      boolean: val => 'boolean' === typeof val,
      number: val => 'number' === typeof val,
      string: val => 'string' === typeof val,
      date: val => '[object Date]' === Object.prototype.toString.call(val),
    },
    result: {},
  });

console.log({
  nullish,
  boolean,
  number,
  string,
  date,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

Edit ... in order to reflect the discussion with @Teemu

As it has been discussed in the comments below, there is another generic reduce based approach which is more strict in terms of type detection due to its straightforward implementation which extracts a type's (internal) class name and collects any array item accordingly.

Here everything depends on a proper type detection which is a very specific field on its own.

// helper function for exposing a value's (internal) class name.
function getClassName(value) {
  const regXClassName =
    // ... [https://regex101.com/r/flUvPh/1]
    (/class\s+(?<customName>[^\s{]+)(?:\s+extends\s+\S+)?\s*\{|function\s+(?<builtInName>[^\s(]+)\s*\(/);

  let customName, builtInName;
  if ((value ?? null) !== null) {

    ({ customName, builtInName } = regXClassName
      .exec(String(Object.getPrototypeOf(value).constructor)).groups ?? {});
  }
  return customName || builtInName || (/\[object\s+(?<className>[^\]]+)\]/)
    .exec(Object.prototype.toString.call(value))
    ?.groups.className;
}
// the above helper is part of the following SO answer
// ... [https://stackoverflow.com/questions/73421732/how-to-know-what-class-inheritance-is-object-from-database/73775470#73775470]


function detectAndCollectTypeByItsClassName(result, type) {
  const key = getClassName(type)
    // first character to lower case.
    .replace(/./, match => match.toLowerCase());

  (result[key] ??= []).push(type);

  return result;
}


console.log(
  ['hhg', 'fthth', 456, true]
    .reduce(detectAndCollectTypeByItsClassName, {})
);
class CustomDate extends Date {}

console.log(
  [null, void 0, 0, 'a', 1, 'b', true, 2, null, false, new Date, new CustomDate]
    .reduce(detectAndCollectTypeByItsClassName, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • A less verbose way to use `reduce` would be `return arr.reduce((a, v, {t = typeof v}) => (a[t]?.push(v) ?? (a[t] = [v]), a), {});` = ). – Teemu Oct 14 '22 at 09:21
  • @Teemu ... The above chosen design on purpose is open to more than just a `typeof` operator based type detection where the latter does neither support `null` values nor object types other than just `'object'`. The approach's introducing explanation and both code examples do emphasize this too. – Peter Seliger Oct 14 '22 at 10:04
  • In the injected argument [you can use a function call](https://jsfiddle.net/0u9o8exh/) instead of `typeof` operator. – Teemu Oct 14 '22 at 10:09
  • @Teemu ... but then one can not use any type detection function which targets a very specific condition and always returns a boolean value like e.g. `isUndefinedOrNull` (nullish). – Peter Seliger Oct 14 '22 at 10:13
  • No? Please add some null or undefined values to `exampleArr` in the fiddle code, and test. Hmm ... For nullish values we need to remove lowercase, and use capitalized "class" names instead. – Teemu Oct 14 '22 at 10:14
  • @Teemu ... I don't need to. I can read it from the fiddle code. One could not collect undefined and null values into one and the same `nullish` values array. One also could not separate Number objects from number types. – Peter Seliger Oct 14 '22 at 10:17
  • Why to collect them to the same array, they're not the same of type? It's also easy to add such a line to the code. Capitalization of the variable names will shadow many of the global constructors, though. – Teemu Oct 14 '22 at 10:21
  • @Teemu ... Thats why I wrote ... _"possible generic approach"_. Your linked fiddle code is **generic _but strict_**. The above presented solution is **generic _and customizable_** and exactly like I wanted it to be. Otherwise I would have written something like your fiddle code. Why not **making the fiddle part of _your answer_** otherwise it might get overlooked. – Peter Seliger Oct 14 '22 at 10:25