-1

This is continuation of this question but for ARRAYS. Suppose I want to call array function with 2 parameters e.g.

console.log(
  [3,4,5].slice(1,2)
)

In first step I reduce characters set to jsfuck convention where we have 6 available characters: []()!+ (and for clarity: with a-z A-Z letters surrounded by " (JS strings) and numbers - which are easy to convert to those 6 chars):

console.log(
  [3,4,5]["slice"](1,2)
)

The problem here was comma (forbidden character) but we can overcome this problem by use following clever technique discovered by trincot:

console.log(
  [1]["concat"](2)["reduce"]([]["slice"]["bind"]([3,4,5]))
)

But the new question arise:

It is possible to call sequence of functions with 2 (or more) parameters without nesting them (which is imposed by above technique) but in "flow" way where we call next function in right side eg.: [3,4,5].slice(1,2).concat(6).. (without using 'eval' like solution where string is interpreted as code) ? (for function with one parameter it is possible e.g.: [3,4,5].slice(1).concat(6) )

j08691
  • 204,283
  • 31
  • 260
  • 272
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
  • 2
    Surely questions about esoteric languages fail the [*"practical"*](https://stackoverflow.com/help/dont-ask) requirement by definition? Seems more like [codegolf.se]. – jonrsharpe Aug 28 '20 at 10:24
  • @jonrsharpe jsfuck is not language - it is subset of JS (same way as JSON) – Kamil Kiełczewski Aug 28 '20 at 10:25
  • Esoteric *styles*, then - just don't write code like that, problem solved. Arbitrary constraints like this are no more useful than the arbitrary constraints of homework questions. – jonrsharpe Aug 28 '20 at 10:25
  • Specifically for `slice`, and only when you have some pre-knowledge of the arguments and array length, the example can be encoded as `[3, 4, 5].reverse().slice(1 /* length-2 */).reverse().slice(1)`. – trincot Aug 28 '20 at 13:29
  • @trincot Your technique works when we can transform [3,4,5] to [[3,4,5]] as follows `[3,4,5].reduce((a,c,i,ar)=> [ar]).concat([[1,2]]).reduce([].slice.apply.bind([].slice))` but the key step is how to do such transformation without eval like here `[3,4,5].reduce((a,c,i,ar)=> [ar]).` – Kamil Kiełczewski Aug 28 '20 at 14:39
  • Yes, indeed, wrapping the array into another is the key problem to solve. – trincot Aug 28 '20 at 14:40
  • 1
    Got it. `[3, 4, 5].reduce([].constructor.of).slice(-1)` gives `[[3, 4, 5]]`. And then you can apply the existing method. – trincot Aug 28 '20 at 14:45
  • Wow! :) write it down as answer – Kamil Kiełczewski Aug 28 '20 at 14:47

3 Answers3

1

The existing technique to wrap the subject of the method inside an array, which is mentioned here, was intended specifically for strings, turning "string" into ["string"]. The trick was using .split() without arguments. After applying that trick, it was not so difficult to use the other little tricks to apply the method that was desired, and allow chaining on the result.

Of course, now that the subject is an array, we cannot use .split().

It turns out that you can use the following method to make it happen for arrays:

console.log(
  [3, 4, 5].reduce([].constructor).slice(-1)
)

This is rather inefficient, as reduce will actually iterate the subject array, destroying the result of the previous iteration, and so only the result of one (the last) iteration determines the result. You get the four arguments, which are passed to the callback, in an array, using the Array constructor (here [].constructor). As the fourth argument is the original array, we could pop it of: but that would leave us with the original array. So we slice it: that way we get the nested array.

Using this with the other tricks, you get this expression for executing [3,4,5].slice(1,2):

console.log(
  [3].concat(4).concat(5).reduce([].constructor)
     .slice(-1)
     .concat([[1].concat(2)])
     .reduce([].fill.apply.bind([].slice))
)

All the method references can be written in ["method"] notation so that you are left with producing these string literals with JSF:

console.log(
  [3]["concat"](4)["concat"](5)["reduce"]([]["constructor"])
     ["slice"](-1)
     ["concat"]([[1]["concat"](2)])
     ["reduce"]([]["fill"]["apply"]["bind"]([]["slice"]))
)

Caveat

This solution relies on the reduce callback being called. Be aware that it is not called when the subject array has only one element. In that case reduce just returns the original array, which is undesired. Also, this method will produce an exception when the subject array is empty.

trincot
  • 317,000
  • 35
  • 244
  • 286
1

Here I develop trincot answer - his nice solution have problem for case when array is empty or has only one element. Using his idea I find solution which solve problem with one element (but still problem with empty array exists).

The key problem: wrap array [1,2,3] into another array to get [[1,2,3]] in flow way (without code wrapping) - to do this we can use following code

console.log(
   [3,4,5].map([].constructor)[0].slice(-1)
)

console.log(
   [3].map([].constructor)[0].slice(-1)
)

So the full solution looks like

console.log(
  [3,4,5]
    // call slice (repeat this pattern for multi-param methods)
    .map([].constructor)[0].slice(-1)
    .concat([[1,2]]).reduce([].slice.apply.bind([].slice))
    // next method call in "flow" style
    .concat(6)   
)

Code after removing commas and dots looks as follows

console.log(
  [3]["concat"](4)["concat"](5)
    ["map"]([]["constructor"])[0]["slice"](-1)
    ["concat"]([[1]["concat"](2)])["reduce"]([]["slice"]["apply"]["bind"]([]["slice"]))
    ["concat"](6)   
)
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
1

Here I develop of my own answer (based on trincot idea) - for clarity I made separate answer. Finally I found solution which works with array with zero, one and more elements. So the key problem: wrap input array [3,4,5] by another array to get [[3,4,5]] in "flow-way" using only right-hand-side operators is solved :)

console.log(
       [].map([].constructor).concat([[[]]])[0].slice(-1)
)

console.log(
      [5].map([].constructor).concat([[[]]])[0].slice(-1)
)

console.log(
  [3,4,5].map([].constructor).concat([[[]]])[0].slice(-1)
)

full solution:

console.log(
  [3,4,5]
    // call slice (repeat this pattern for multi-param methods)
    .map([].constructor).concat([[[]]])[0].slice(-1)
    .concat([[1,2]]).reduce([].slice.apply.bind([].slice))
    // next method call in "flow" style
    .concat(6)   
)

Final code after removing commas, dots

console.log(
  [3]["concat"](4)["concat"](5)
    ["map"]([]["constructor"])["concat"]([[[]]])[0]["slice"](-1)
    ["concat"]([[1]["concat"](2)])["reduce"]([]["slice"]["apply"]["bind"]([]["slice"]))
    ["concat"](6)   
)
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345