22
\$\begingroup\$

To neutralize data, recursively replace all numbers (not digits!) with zeros and all characters (not strings!) with spaces.

You must accept any single number, character, or string, or possibly recursive array* of mixed numeric/character data that your language can handle. For example, you must accept actual strings (and not restrict your input to lists of single characters) if your language can handle such.

If your language contains a built-in which does all or most of this task, I would appreciate an additional alternative version without it.

The shortest submission in each language is a winner, and will receive an upvote from me.

Sample cases

  1. """"

  2. 70

  3. 123.4560

  4. "X"" "

  5. " "" "

  6. "Yo!"" "

  7. [][]

  8. [-1.2E3][0]

  9. ["Hey"][" "]

  10. ["H","e","y"][" "," "," "]

  11. ["R",2,"D",2][" ",0," ",0]

  12. ["C","3","P",0][" "," "," ",0]

  13. ["THX",[1138]][" ",[0]]

  14. ["T","H","X",[1138]][" "," "," ",[0]]

  15. [[["H"],"e",1,1,0],[[-3],"arth"]][[[" "]," ",0,0,0],[[0]," "]]

* If your language has several types which can equally well represent arrays like the above examples, you may chose to support only one. Two entries can both win, even though they are using the same language, by each going with a different data type.

\$\endgroup\$
8
  • \$\begingroup\$ What if our language doesn't distinguish between characters and length-1 strings? \$\endgroup\$
    – xnor
    Jan 16, 2017 at 9:48
  • \$\begingroup\$ @xnor AFAICT the effect would be the same. \$\endgroup\$
    – Adám
    Jan 16, 2017 at 9:52
  • \$\begingroup\$ Oh, I see that now from the test cases, but it wasn't clear to me that meant to take every string element and replace each of its characters with spaces. Actually, I didn't interpret that the replacements have to be done recursively at all. The spec talks about neutralizing an array, but it looks like you want single elements not in an array to be acted on as well? \$\endgroup\$
    – xnor
    Jan 16, 2017 at 9:54
  • \$\begingroup\$ @xnor Correct. Feel free to edit the question to better reflect this. \$\endgroup\$
    – Adám
    Jan 16, 2017 at 9:58
  • \$\begingroup\$ Let us continue this discussion in chat. \$\endgroup\$
    – Adám
    Jan 16, 2017 at 10:53

17 Answers 17

17
\$\begingroup\$

JavaScript (ES6), 53 47 bytes

f=o=>o.big?o.replace(/./g,' '):o.map?o.map(f):0

Test cases

f=o=>o.big?o.replace(/./g,' '):o.map?o.map(f):0

test = [
  "",
  7,
  123.456,
  "X",
  "  ",
  "Yo!",
  [],
  [-1.2E3],
  ["Hey"],
  ["H","e","y"],
  ["R",2,"D",2],
  ["C","3","P",0],
  ["THX",[1138]],
  ["T","H","X",[1138]],
  [[["H"],"e",1,1,0],[[-3],"arth"]]
];

test.map(o => {
  console.log(JSON.stringify(o), '=>', JSON.stringify(f(o)));
});

\$\endgroup\$
3
  • \$\begingroup\$ Finally, a use for big()! Although, you'd probably provide better future-proofing using raw() instead. \$\endgroup\$
    – Shaggy
    May 16, 2017 at 12:02
  • \$\begingroup\$ @Shaggy Hmm. Which syntax would you use with raw()? I don't think String.prototype.raw() is defined. Only String.raw() is. \$\endgroup\$
    – Arnauld
    May 16, 2017 at 13:21
  • \$\begingroup\$ Oh, yeah, sorry. \$\endgroup\$
    – Shaggy
    May 16, 2017 at 13:22
7
\$\begingroup\$

Python 2, 52 bytes

f=lambda x:1-(x<{})and map(f,x)if x<''else' '*len(x)

Try it online!

How it works

Python lets you compare different types. Numeric types are always smaller than iterables, and iterables are sorted by their type names, so

0 < {} < [] < '' < ()

This way, f does the following.

  • If x is numeric x<{} returns True and 1-(x<{}) returns 0. The code after and does not get executed.

  • If x is iterable, 1-(x<{}) returns 1 (truthy), so the code after and gets executed.

    • If x is a list, x<'' is true and f gets mapped over its items.

    • If x is a string, x<'' is false and x is replaced with a string of spaces of the same length.

\$\endgroup\$
7
\$\begingroup\$

Ruby, 54 53 49 bytes

f=->a{a*0==0?0:a*0==[]?a.map{|x|f[x]}:" "*a.size}

Maybe there is a better way, but:

  • x*0 == 0 for integers
  • x*0 == "" for strings
  • x*0 == [] for arrays
\$\endgroup\$
3
  • \$\begingroup\$ Clever! Thanks for giving me a way to determine types in Python as well! \$\endgroup\$
    – TidB
    Jan 16, 2017 at 14:37
  • \$\begingroup\$ it doesn't work for the string and number examples outside of an array, but if you change a.map to [*a].map it works as expected \$\endgroup\$ Jan 17, 2017 at 20:20
  • \$\begingroup\$ This has been fixed now, thanks. \$\endgroup\$
    – G B
    Jan 18, 2017 at 7:57
6
\$\begingroup\$

Mathematica, 51 bytes

I've currently got two solutions at this byte count:

#/._?NumberQ->0/.x_String:>StringReplace[x,_->" "]&
StringReplace[#,_->" "]~Check~#&//@#/._?NumberQ->0&

The second one throws a bunch of warnings which can be ignored.

Explanation

In either case, we start by turning numbers into zeros with

#/._?NumberQ->0

Then to process the string, there are two options. Either, we use another substitution that only applies to strings:

.../.x_String:>StringReplace[x,_->" "]

Or, we use the MapAll operator //@ which maps a function over every single element in a nested list. The catch here is that we'll be trying to use StringReplace on zeros as well as the symbol List (since //@ also goes through the heads of expressions), so we need to use Check (sort of like a catch statement in other languages) to avoid wreaking havoc with those values:

StringReplace[#,_->" "]~Check~#&//@...
\$\endgroup\$
5
\$\begingroup\$

Jelly, 4 bytes

nOa⁶

This is a monadic link. Jelly's implicit printing does a lot of splatting; to verify that the output is what it should be, you can view the internal representation with ŒṘ. Note that this exposes strings as lists of characters, which is how the Jelly interpreter implements them.

Try it online!

How it works

nOa⁶  Monadic link. Argument: z

 O    Ordinal; replace all characters with the code points.
      This does not affect numbers.
n     Vectorizing not-equal; compare z with the result to the right, replacing
      characters with 1 and numbers with 0.
  a⁶  Logical AND space; replace all 1's with spaces.
\$\endgroup\$
6
  • \$\begingroup\$ I was waiting for an APL to come up. Now chose a different one for a 1-byte solution! \$\endgroup\$
    – Adám
    Jan 16, 2017 at 17:53
  • \$\begingroup\$ Thanks for the heads up, but I'm not sure I even know how to represent [[["H"],"e",1,1,0],[[-3],"arth"]] in APL... \$\endgroup\$
    – Dennis
    Jan 16, 2017 at 18:50
  • \$\begingroup\$ JSON2APLexpr←⎕SE.Dyalog.Utils.repObj 7159⌶ ⋄ JSON2APLexpr'[[["H"],"e",1,1,0],[[-3],"arth"]]' gives ((,⊂,'H') (,'e') 1 1 0) ((,¯3) 'arth'). FYI, 7159⌶ will be ⎕JSON in version 16.0. \$\endgroup\$
    – Adám
    Jan 16, 2017 at 18:57
  • \$\begingroup\$ APL can tackle any JSON. However, plenty of APL data cannot be unambiguously represented in JSON. \$\endgroup\$
    – Adám
    Jan 16, 2017 at 18:59
  • \$\begingroup\$ Do you think a code-golf which involves parsing or maybe manipulating APL arrays would be interesting? \$\endgroup\$
    – Adám
    Jan 17, 2017 at 9:44
5
\$\begingroup\$

Perl 6, 34 48 bytes

{.deepmap:{$_~~Str??" "x.comb!!0}}

{$_~~List??$_».&?BLOCK!!$_~~Str??" "x .comb!!0}

Expanded:

{                          # block-based lambda
    $_ ~~ List             # if argument is a list/array
        ?? $_».&?BLOCK     # then recurse for each element (&?BLOCK is a compile-time var)
        !! $_ ~~ Str       # else if argument is a string
            ?? " "x .comb  # then use space repeated by the string's length,
            !! 0           # else use the number 0
}
\$\endgroup\$
2
  • \$\begingroup\$ The .deepmap doesn't work correctly for singular inputs like "X" or 7, because it always returns a List. \$\endgroup\$ Jan 16, 2017 at 16:22
  • \$\begingroup\$ Darn, I must have overlooked that (or the task was clarified later). This makes it quite a bit longer. \$\endgroup\$
    – smls
    Jan 17, 2017 at 7:06
3
\$\begingroup\$

GAP, 91 bytes

GAP has a method Zero that returns the neutral additive element corresponding to an element of an additive structure. That handles numbers and even lists of numbers which are considered vectors, but not arbitrary lists. So let's add these and Chars and use that Strings are lists of Chars:

InstallOtherMethod(Zero,[IsChar],c->' ');
InstallOtherMethod(Zero,[IsList],l->List(l,Zero));

(I'm not counting the newline as it isn't needed.) Of course this is far away from the intended use of Zero, and GAP would complain if I hadn't used InstallOtherMethod. Now I can do:

gap> Zero([[["H"],"e",1,1,0],[[-3],"arth"]]);
[ [ [ " " ], " ", 0, 0, 0 ], [ [ 0 ], "    " ] ]

I wouldn't say that the builtin does most of the job, one might rather suspect that writing a normal function should be shorter, but my best try doing that was 92 bytes long:

f:=function(x)if IsInt(x)then return 0;elif IsList(x)then return List(x,f);fi;return' ';end;
\$\endgroup\$
2
\$\begingroup\$

Haskell, 115 bytes

My language cannot handle any mix of numbers and strings in lists (but of course you can define a sum type and put that in a list), and its standard lists cannot handle differently nested lists. So I'm just doing what it can handle. I think that is not unfair because it is still long, and I mainly do it to show features of haskell that are rarely seen in haskell golfing solutions. Note that Strings are lists of Chars.

class N a where n::a->a
instance N Double where n _=0
instance N Char where n _=' '
instance N a=>N[a]where n=map n

This handles any number as Double:

*Main> n 42
0.0
*Main> n 123.456
0.0
*Main> n "hi"
"  "
*Main> n [[1,2],[3,4,5]]
[[0.0,0.0],[0.0,0.0,0.0]]
*Main> n ["hello","world!"]
["     ","      "]
\$\endgroup\$
5
  • 1
    \$\begingroup\$ It should be shorter to define a data structure with data N=S[Char]|I Int|L[N] and a recursive function on that. \$\endgroup\$
    – Zgarb
    Jan 16, 2017 at 12:35
  • \$\begingroup\$ You're probably right I guess, but that's less interesting and still looks like not really solving the problem (I admit it actually is closer). I'll leave that for someone else. \$\endgroup\$ Jan 16, 2017 at 12:53
  • \$\begingroup\$ integers? How about test case 3.? \$\endgroup\$
    – Adám
    Jan 16, 2017 at 12:57
  • \$\begingroup\$ @Adám Oh you're right, and unfortunately I can't claim my language doesn't have those. Can I only handle Doubles? I can input them without decimal point. \$\endgroup\$ Jan 16, 2017 at 13:21
  • \$\begingroup\$ @ChristianSievers Yes, that's fine. Basically, you should be able to handle whatever an import (if possible) from the given JSON would give you. \$\endgroup\$
    – Adám
    Jan 16, 2017 at 13:22
2
\$\begingroup\$

PHP, 91 bytes

function f($a){return!is_array($a)?is_string($a)?str_pad("",strlen($a)):0:array_map(f,$a);}

if parameter is array: recurse using array_map.
else if parameter is string: generate a strings of spaces with the same length.
else 0.

is_string saves one byte over is_numeric; negating is_array() renders parentheses obsolete. Altogether it´s 17 bytes shorter than if()recursion-loop;else x?string:number with a call by reference.

\$\endgroup\$
2
\$\begingroup\$

Python 2, 59 bytes

g=lambda d:0if d*0==0else " "*len(d)if d*0==""else map(g,d)

uses G B's way to determine types

\$\endgroup\$
0
2
\$\begingroup\$

 Common Lisp, 87

(defun c(e)(typecase e(list(mapcar'c e))(number 0)(string(map'string(lambda(u)#\ )e))))

Ungolfed

(defun c(e)
  (typecase e
    (list (mapcar #'c e))
    (number 0)
    (string (map 'string (lambda(u) #\space) e))))

Example

> (c '((("H") "e" 1 1 0) ((-3) "arth")))
(((" ") " " 0 0 0) ((0) "    "))
\$\endgroup\$
2
\$\begingroup\$

APL (Dyalog) 13.2 and earlier, 1 byte

Until and including version 13.2, monadic did exactly this. The old behaviour can be activated by setting ⎕ML (Migration Level) to zero.

Try it online!

Without using old behaviour it is 4 bytes:

⊃0⍴⊂

Try it online!

 enclose

0⍴ make a length-zero list of copies of the entire input (preserves only structure and type info)

 coerce one element from that (creates a prototypical array)

\$\endgroup\$
1
\$\begingroup\$

Groovy, 53 bytes

{[it].collectNested{it in String?" "*it.size():0}[0]}

This is an unnamed closure. Try it here!

Explanation:

Groovy has this method .collectNested which helps to iterate through a list as if it was flattened.

To handle special cases where only an integer or a string is passed without a list, simply wrap every input into a list and output the first element.

\$\endgroup\$
1
\$\begingroup\$

Pyke, 8 bytes (Old version)

.FZ*0+d&

Explanation:

.FZ*0+d& - for i in deep_for(input):
  Z*     -    i*0
    0+   -   ^ + 0
      d& -  ^ and " "

This no longer works as an update made it so falsies of other types where there are truthies automatically convert to the falsey of the truthy type.

\$\endgroup\$
1
  • \$\begingroup\$ That's of course acceptable. \$\endgroup\$
    – Adám
    Jan 16, 2017 at 18:47
1
\$\begingroup\$

C#, 197 195 Bytes

a=>{if(a is string||a is char)return new string(' ',(a as string)?.Length??1);try{System.Convert.ToDecimal(a);return 0;}catch{}var b=(object[])a;for(int i=0;i<b.Length;)b[i]=n(b[i++]);return b;};

This function handles char, string, any builtin number type and recursive arrays.

Full program, ungolfed whit output helper:

using System.Linq;    
class Class
{
    public static void Main()
    {
        System.Func<object, object> n = null;
        n = a => 
        {
            if (a is string || a is char)
                return new string(' ', (a as string)?.Length ?? 1);
            try
            {
                System.Convert.ToDecimal(a);
                return 0;
            }
            catch { }

            var b = (object[])a;
            for (int i = 0; i < b.Length;)
                b[i] = n(b[i++]);
            return b;
        };

        var result = n(new object[] { new object[] { new object[] { "H" }, 'e', 1.5, 1, 0 }, new object[] { new object[] { -3 }, "arth" } });
        System.Console.WriteLine(Output(result));
        System.Console.Read();
    }

    static string Output(object x)
    {
        var arr = x as object[];
        if (arr != null)
            return "[" + string.Join(",", arr.Select(Output)) + "]";
        else
            return x.ToString();
    }
};

The using statement is only needed for the output helper, not for the actual function.

Try Online

\$\endgroup\$
0
\$\begingroup\$

Javascript ES6, 81 chars

f=(x,t)=>(t=g=>eval(`try{x.${g}}catch(e){}`))`replace(/./g,' ')`||t`map(f)`||x&&0

Test:

f=(x,t)=>(t=g=>eval(`try{x.${g}}catch(e){}`))`replace(/./g,' ')`||t`map(f)`||x&&0

console.log(
`"" → ""
7 → 0
123.456 → 0
"X" → " "
"  " → "  "
"Yo!" → "   "
[] → []
[-1.2E3] → [0]
["Hey"] → ["   "]
["H","e","y"] → [" "," "," "]
["R",2,"D",2] → [" ",0," ",0]
["C","3","P",0] → [" "," "," ",0]
["THX",[1138]] → ["   ",[0]]
["T","H","X",[1138]] → [" "," "," ",[0]]
[[["H"],"e",1,1,0],[[-3],"arth"]] → [[[" "]," ",0,0,0],[[0],"    "]]`
.split`
`.map(s => s.split` → `.map(eval))
.every(([s,k]) => JSON.stringify(f(s))==JSON.stringify(k)))

\$\endgroup\$
0
\$\begingroup\$

Java 7, 262 (268) bytes

import java.util.*;Object c(Object o){if(o instanceof List){List r=new ArrayList();for(Object x:(List)o)r.add(c(x));return r;}if(o instanceof String){String r="\"";for(int i=((String)o).length();i-->0;r+=" ");return r+"\"";}return o instanceof Number?0:o instanceof Character?"' '":"";}

282 + 6 for the added \" and ' to pretty-print the String and characters.

Explanation:

import java.util.*;            // Required import for List and ArrayList
Object c(Object o){            // Method with Object parameter and Object return-type
  if(o instanceof List){       //  If the input is a List
    List r=new ArrayList();    //   Create a result-list
    for(Object x:(List)o)      //   Loop over the items of the input-list
      r.add(c(x));             //    And add all items with a recursive-call to this method
                               //   End of loop (implicit / single-line body)
    return r;                  //   Return result-list
  }if(o instanceof String){    //  If the input is a String instead
    String r="\"";             //   Create a result-String (starting with `"`
    for(int i=((String)o).length();i-->0;r+=" ");
                               //   Replace all characters in the String with a space
    return r+"\"";             //   Return the result within double-quotes
  }
  return o instanceof Number?  //  If the input is an integer or decimal instead:
    0                          //   Return 0
   :o instanceof Character?    //  If the input is a character instead:
    "' '"                      //   Return ' '
   :                           //  Else:
    "";                        //   Return an empty String
}                              // End of method

Test code:

Try it here.

import java.util.*;
class M{
  static Object c(Object o){if(o instanceof List){List r=new ArrayList();for(Object x:(List)o)r.add(c(x));return r;}if(o instanceof String){String r="\"";for(int i=((String)o).length();i-->0;r+=" ");return r+"\"";}return o instanceof Number?0:o instanceof Character?"' '":"";}
  
  public static void main(String[] a){
    System.out.println(c(""));
    System.out.println(c(7));
    System.out.println(c(123.456));
    System.out.println(c('X'));
    System.out.println(c("  "));
    System.out.println(c("Yo!"));
    System.out.println(c(new ArrayList()));
    System.out.println(c(new ArrayList(){{add(-1.2e3);}}));
    System.out.println(c(new ArrayList(){{add("Hey");}}));
    System.out.println(c(new ArrayList(){{add('H');add('e');add('y');}}));
    System.out.println(c(new ArrayList(){{add('R');add(2);add('D');add(2);}}));
    System.out.println(c(new ArrayList(){{add("THX");add(new ArrayList(){{add(1138);}});}}));
    System.out.println(c(new ArrayList(){{add('T');add('H');add('X');add(new ArrayList(){{add(1138);}});}}));
    System.out.println(c(new ArrayList(){{add(new ArrayList(){{add(new ArrayList(){{add('H');}});add('e');add(1);add(1);add(0);}});add(new ArrayList(){{add(new ArrayList(){{add(-3);}});add("arth");}});}}));
  }
}

Output:

""
0
0
' '
"  "
"   "
[]
[0]
["   "]
[' ', ' ', ' ']
[' ', 0, ' ', 0]
["   ", [0]]
[' ', ' ', ' ', [0]]
[[[' '], ' ', 0, 0, 0], [[0], "    "]]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.