20
\$\begingroup\$

Given a string of printable ASCII text (including newlines and spaces) that contains at least one character that is neither a newline nor a space, output a truthy value if the string is rectangular, and a falsey value otherwise. Additionally, the source code for your solution must be rectangular.

A string is rectangular if it meets all of the following conditions:

  1. The first line and the last line contain no spaces.
  2. The first and last character of each line is not a space.
  3. All lines have the same number of characters.

For example, the following text is rectangular:

abcd
e fg
hijk

This text, however, is not rectangular (requirement #3):

1234
567
8900

Test Cases

Truthy:

sdghajksfg
asdf
jkl;
qwerty
u i op
zxcvbn
1234
5 6
7890
abcd
e fg
hijk

Falsey:

a b c
123
456
7 9
12
345
qwerty
 uiop
zxcvnm
1234
567
8900

This is , so the shortest solution in bytes wins.

asked May 2, 2018 at 13:25
\$\endgroup\$
10
  • \$\begingroup\$ Sandbox \$\endgroup\$ Commented May 2, 2018 at 13:25
  • 2
    \$\begingroup\$ Related. \$\endgroup\$ Commented May 2, 2018 at 13:41
  • 9
    \$\begingroup\$ So, a one-liner without any space is a valid submission, correct? \$\endgroup\$ Commented May 2, 2018 at 13:45
  • 1
    \$\begingroup\$ related \$\endgroup\$ Commented May 2, 2018 at 14:34
  • 1
    \$\begingroup\$ Can we take input as an array of strings, one for each line? Or must we input a single long string that includes the line breaks? \$\endgroup\$ Commented May 2, 2018 at 14:46

19 Answers 19

11
\$\begingroup\$

C (gcc), (削除) 127 (削除ここまで) (削除) 125 (削除ここまで) (削除) 124 (削除ここまで) 118 bytes

  • Saved two bytes by golfing r*=!e&(!t|t==c); to r>>=e||t&&t-c;. (This golf was the inspiration for my recent C tips answer Inverse flag update.)
  • Saved a byte by golfing *(_-2) to _[~1].
  • Saved six bytes by golfing *_++-10||(...) to *_++<11?...:0 and utilizing the placeholder zero ...:0 (which is not used constructively) to golf the c++ increment. Those golfs allowed some further loop reshuffling.
  • When one can use multiple falsey values, 114 bytes could be possible.
r,e,c,t;_(char*_){for(r=1,t=c=0;*_;*_++<11?r*=(t||(t=c,!e))&*_>32&_[~1]>32&t==c,c=e=0:c++)*_-32||(e=1);r>>=e||t&&t-c;}

Try it online!

Source layout achieving a taller rectangle.

Explanation

The following explains the 124 bytes long version.

r,e,c,t;_(char*_){ // `r` is the boolean result flag, `e` a boolean flag if the current line contains
 // a space, `t` the first line's width, `c` the current line's current width
 for(r=1,t=c=0;*_;c++) // initialize, loop through entire string
 *_-32|| // if the current char is a space,
 (e=1), // the current line contains a space
 *_++-10|| // if the current char is a newline (char pointer `_` now incremented)
 (r*=(t||(t=c,!e)) // if t is not yet set, the current line is the first line; set it
 // to this line's length, check that no spaces where found
 &*_>32 // the next line's first char should not be a space
 &_[~1]>32 // this line's last char should not have been a space
 &t==c,c=~0,e=0); // the line lengths should match, reset `c` and `e` to zero
 // (`~0 == -1`, countering the loop's increment of `c`)
 r>>=e||t&&t-c;} // return boolean flag, check that the last line does not contain spaces,
 // there was either no newline or line lengths match
 // (here) equivalent to `r*=!e&(!t|t==c)`

Try it online!

answered May 2, 2018 at 14:01
\$\endgroup\$
0
4
\$\begingroup\$

Java 10, (削除) 214 (削除ここまで) (削除) 176 (削除ここまで) (削除) 169 (削除ここまで) (削除) 152 (削除ここまで) (削除) 144 (削除ここまで) 139 bytes

s->{String[]a=s.split("\n")
;int r=1,i=0,R=a.length;for
(;i<R;i++)if(i<1|i>R-2?a[i]
.contains(" "):a[i].trim( )
!=a[i])r=0;return-r<0;}////

-5 bytes thanks to @Neil.

Uses String[]a instead of var a; return-r<0; instead of return r>0;; and added a comment // at the very end, so there aren't spaces on the first and last rows.

Note that this rectangle is shorter than a single-line input, because int r=1,...; should be replaced with int[]v{1,...};, and all uses of the integers would then becomes v[n] (where n is the index of the variable in the array v).

Try it online.

Explanation:

s->{ // Method with String parameter and boolean return-type
 String[]a=s.split("\n"); // Input split by new-lines
 int r=1, // Result-integer, starting at 1
 i=0, // Index `i`, starting at 0
 R=a.length; // Amount of rows `R`
 for(;i<R;i++) // Loop `i` over the rows
 if(i<1 // If it's the first row,
 |i>R-2? // or the last row:
 a[i].contains(" ") // And the current row contains a space
 :a[i].trim()!=a[i]) // Or either column of the current row contains a space
 r=0; // Set the result `r` to 0
 return-r<0;} // Return whether `r` is still 1
//// // Comment to comply to the rules of the challenge

Here is the same base program with spaces ((削除) 128 (削除ここまで) 126 bytes):

s->{var a=s.split("\n");int r=1,i=0,R=a.length;for(;i<R;i++)if(i<1|i>R-2?a[i].contains(" "):a[i].trim()!=a[i])r=0;return r>0;}

-2 bytes thanks to @Neil.

Try it online.

answered May 2, 2018 at 14:09
\$\endgroup\$
1
  • 1
    \$\begingroup\$ tio.run/… \$\endgroup\$ Commented May 2, 2018 at 16:36
3
\$\begingroup\$

T-SQL, (削除) 237 (削除ここまで) 207 bytes

SELECT(SELECT(IIF(max(len(v))=min(len(v)),1,0)*IIF(SUM(len(v+'x')-len
(trim(v))-1)=0,1,0))FROM t)*(SELECT(IIF(SUM(charindex(' ',v))=0,1,0))
FROM[t]WHERE[i]IN(SELECT(min(i))FROM[t]UNION(SELECT(max(i))FROM[t])))

Outputs 1 for rectangular, 0 otherwise. I had to use tons of extra parens and brackets to eliminate spaces, I'm sure there is vast room for improvement.

Explanation:

Per our allowed I/O options and clarification in the question comments, input is taken as separate rows in a pre-existing table t. Because data in SQL is inherently unordered, that table includes a "row number" identity field i:

CREATE TABLE t (i INT IDENTITY(1,1), v VARCHAR(999))

Basically my SQL performs 3 subqueries, each of which returns 0 or 1 based on the 3 criteria of "rectangular" code. Those 3 values are multiplied together, only returning 1 for code that satisfies all 3.

EDIT: Combined criteria 2 and 3 into the same SELECT to save space

SELECT(
SELECT(IIF(max(len(v))=min(len(v)),1,0) --All rows same length
 *IIF(SUM(len(v+'x')-len(trim(v))-1)=0,1,0))FROM t) --no leading or trailing spaces
*(SELECT(IIF(SUM(charindex(' ',v))=0,1,0)) --No spaces at all in
FROM[t]WHERE[i]IN(SELECT(min(i))FROM[t] -- first row or
 UNION(SELECT(max(i))FROM[t]))) -- last row

The TRIM(v) function is only supported by SQL 2017 and above. Earlier versions would need LTRIM(RTRIM(v)), which would require rebalancing the rows.

One random note: the LEN() function in SQL ignores trailing spaces, so LEN('foo ') = 3. To get a "true" length you have to tack a character on to the end then subtract one :P

answered May 3, 2018 at 16:33
\$\endgroup\$
3
\$\begingroup\$

C++, (削除) 199 (削除ここまで) (削除) 183 (削除ここまで) (削除) 181 (削除ここまで) 175 bytes

This template function accepts lines as a collection of strings (which may be wide strings), passed as a pair of iterators.

#include<algorithm>//
template<class I>bool
f(I a,I b){return!~+(
*a+b[-1]).find(' ')&&
std::all_of(a,b,[&a](
auto&s){return' '+-s.
back()&&s[0]-' '&&a->
size()==s.size();});}

Thanks are due to user Erroneous for reminding me of the back() member of std::string and for pointing out that npos+1 is zero.

Ungolfed equivalent

The only real golfing is to concatenate the first and last lines so we can perform a single find for spaces in those.

#include <algorithm>
template<class It>
bool f(It a, It b)
{
 return (*a+b[-1]).find(' ') == a->npos
 && std::all_of(a, b,
 [=](auto s) {
 return s.back() != ' '
 && s.front() != ' '
 && s.size() == a->size(); });
}

Test program

#include <iostream>
#include <string>
#include <vector>
int expect(const std::vector<std::string>& v, bool expected)
{
 bool actual = f(v.begin(), v.end());
 if (actual == expected) return 0;
 std::cerr << "FAILED " << (expected ? "truthy" : "falsey") << " test\n";
 for (auto const& e: v)
 std::cerr << " |" << e << "|\n";
 return 1;
}
int expect_true(const std::vector<std::string>& v) { return expect(v, true); }
int expect_false(const std::vector<std::string>& v) { return expect(v, false); }
int main()
{
 return
 // tests from the question
 + expect_true({"sdghajksfg"})
 + expect_true({"asdf", "jkl;",})
 + expect_true({"qwerty", "u i op", "zxcvbn",})
 + expect_true({"1234", "5 6", "7890",})
 + expect_true({"abcd", "e fg", "hijk",})
 + expect_false({"a b c",})
 + expect_false({"123", "456", "7 9",})
 + expect_false({"12", "345",})
 + expect_false({"qwerty", " uiop", "zxcvnm",})
 + expect_false({"1234", "567", "8900",})
 // extra tests for leading and trailing space
 + expect_false({"123", " 56", "789"})
 + expect_false({"123", "45 ", "789"})
 // the function source
 + expect_true({"#include<algorithm>//",
 "template<class I>bool",
 "f(I a,I b){return!~+(",
 "*a+b[-1]).find(' ')&&",
 "std::all_of(a,b,[&a](",
 "auto&s){return' '+-s.",
 "back()&&s[0]-' '&&a->",
 "size()==s.size();});}",})
 ;
}
answered May 3, 2018 at 10:50
\$\endgroup\$
1
  • \$\begingroup\$ This can be further golfed to 183 bytes with a line width of 22, using .find(' ')+1==0, and s.back() instead of *s.rbegin(). \$\endgroup\$ Commented May 3, 2018 at 16:16
2
\$\begingroup\$

JavaScript (Node.js), 85 bytes

x=>(x=x.split`\n`).some(s=>s.length-x[0].length|s.trim()!=s)<!/\s/.test(x[0]+x.pop())

Try it online!

answered May 2, 2018 at 14:02
\$\endgroup\$
2
  • \$\begingroup\$ @KevinCruijssen sorry :( \$\endgroup\$ Commented May 2, 2018 at 14:48
  • \$\begingroup\$ I love your NOR operator! \$\endgroup\$ Commented May 2, 2018 at 16:27
2
\$\begingroup\$

Python 2, 82 bytes

lambda*s:len(set(map(len,s)))<2<'!'<=min(tuple(s[0]+s[-1])+zip(*s)[0]+zip(*s)[-1])

Try it online!

Invoke as f("abcd", "e fg", "hijk").

answered May 3, 2018 at 9:41
\$\endgroup\$
2
\$\begingroup\$

Haskell, (削除) 106 (削除ここまで) (削除) 102 (削除ここまで) (削除) 98 (削除ここまで) (削除) 110 (削除ここまで) (削除) 109 (削除ここまで) 102 bytes

(\a->all(==[])a||and(e((1<$)<$>a):map(all(>='!').($a))[head,last,map$last,map$head]));e(a:s)=all(==a)s

Thanks to @nimi and @Laikoni for a byte each!

Try it online!

answered May 2, 2018 at 13:55
\$\endgroup\$
0
2
\$\begingroup\$

Haskell, 79 bytes

g(x:r)=all((==(0<$x)).(0<$))r&&all(>='!')(x++last(x:r)++(head<$>r)++(last<$>r))

Try it online! Takes input as a list of lines.

The pattern g(x:r)= ... binds the first line to x and the (possibly empty) list of remaining lines to r. Then all((==(0<$x)).(0<$))r checks if all lines in r have the same length as x (Using this tip).

If not, then the conjunction && short-circuits and returns False, otherwise the right hand side is evaluated. There a string is build which consists of x for the first line, last(x:r) for the last line of r (or the first line again in case r is empty) and (head<$>r) for the first and (last<$>r) for the last character of each line. For this string, all(>='!') checks that it does not contain any spaces (we can't use (>' ') because of the source code restriction).

answered May 3, 2018 at 6:59
\$\endgroup\$
3
  • \$\begingroup\$ Errors on "\n\n" \$\endgroup\$ Commented May 3, 2018 at 11:18
  • \$\begingroup\$ @Angs Good catch. Luckily, OP clarified that the input contains at least one character that is neither a newline nor a space, which also allows to drop the empty list case. \$\endgroup\$ Commented May 3, 2018 at 12:57
  • \$\begingroup\$ Oh nice, didn't notice that being added \$\endgroup\$ Commented May 3, 2018 at 13:02
2
\$\begingroup\$

MATL, 13 bytes

ctgF6Lt&()32>

Input is an array of strings, in the format {'abc' 'de'}.

Output is an array containing only ones, which is truthy, or an array containing at least a zero, which is falsey.

Try it online! Or verify all test cases, including truthiness/falsihood test.

Explanation

c % Implicit input. Convert to char. This concatenates the
 % strings of the input cell array as rows of a rectangular
 % char array, right-padding with spaces as needed
tg % Duplicate, convert to logical. Gives a logical array with
 % the same size containing true in all its entries
F % Push false
6L % Push the array [2, j-1], where j is the imaginary unit.
 % When used as an index, this is interpreted as 2:end-1
t % Duplicate
&( % Assignment indexing with 4 inputs: original array, new
 % value, two indexing arrays. This writes false at the inner
 % rectangle (2:end-1)×ばつ(2:end-1) of the logical array that
 % initially only contained true. This will be used as a
 % logical index (mask) into the rectangular char array
) % Reference indexing. This selects the border of the char
 % array. The result is a column vector of chars
32> % Is each entry greater than 32? (ASCII code for space)
 % Implicit display
answered May 3, 2018 at 13:23
\$\endgroup\$
3
  • \$\begingroup\$ 11 bytes: cO6Lt&(32=~ Try it online! Just nulls out the non-border parts, then checks if there are any spaces. \$\endgroup\$ Commented Jul 10, 2018 at 18:16
  • \$\begingroup\$ @sundar Good idea! That's different enough, post it yourself \$\endgroup\$ Commented Jul 10, 2018 at 18:38
  • 1
    \$\begingroup\$ Nah, feels too similar to your answer, especially if I write it as cF6Lt&(32=~. Feel free to edit it in, or if not we can just leave it in the comments. \$\endgroup\$ Commented Jul 12, 2018 at 8:40
1
\$\begingroup\$

JavaScript (ES6), 88 bytes

s=>!s.split`\n`.some((s,i,a)=>s[L='length']-a[0][L]|(++i%a[L]>1?/^\s|\s$/:/\s/).test(s))

Try it online!

answered May 2, 2018 at 13:57
\$\endgroup\$
1
\$\begingroup\$

Canvas, (削除) 17 (削除ここまで) 15 bytes

4[↷K;}┐){SL]∑4≡

Try it here!

Explanation (ASCII-fied for monospace):

4[↷K;}┐){SL]∑4= full program; pushes the input to the stack.
4[ } repeat 4 times
 ↷ rotate ToS clockwise. This also pads the input with spaces
 K; take off the last line and put it below the item
 ┐ pop the remaining of the input (the center)
 ) and wrap the rest (the sides) in an array
 { ] map over those
 S split on spaces - should result to one item in the array
 L and get the length
 ∑ sum those lengths together
 4= check if equal 4
answered May 2, 2018 at 13:54
\$\endgroup\$
2
  • 4
    \$\begingroup\$ I find it ironic that these UTF8 characters in a monospace-like font give the feeling that there are many spaces in the source. (At least, they do in my browser.) \$\endgroup\$ Commented May 2, 2018 at 14:07
  • 1
    \$\begingroup\$ @Arnauld fullwidth characters do that. And that's why I made a font for my interpreter to make them prettier :p \$\endgroup\$ Commented May 2, 2018 at 14:13
1
\$\begingroup\$

Perl 5, 70 bytes

$f||=$_;$l||=y///c;,ドル||=/^\s|\s$/||$l-y///c;$e=$_}{$\="$f$e"=~/\s/||,ドル

Try it online!

Outputs 0 for truthy, any other number for falsey.

answered May 2, 2018 at 20:26
\$\endgroup\$
1
\$\begingroup\$

Red, (削除) 216 (削除ここまで) 191 bytes

func[s][d:(length?(first(s:(split(s)"^/"))))sp:
func[a][none = find a" "]b: on foreach c s[b: b
and(d = length? c )and(c/1 <>" ")and(" "<> last
c)]res:(sp(first(s)))and(sp(last(s)))and(b)res]

Try it online!

I put a lot of otherwise not necessary parentheses in the first and last rows.

answered May 3, 2018 at 11:06
\$\endgroup\$
0
\$\begingroup\$

Jelly, 17 bytes

Ỵμ.ịЀ;ịɗẎ6e<L€E$

Try it online!

answered May 2, 2018 at 14:07
\$\endgroup\$
5
  • \$\begingroup\$ @JonathanFrech Ah, fixed. >_> \$\endgroup\$ Commented May 2, 2018 at 14:26
  • \$\begingroup\$ @MagicOctopusUrn Huh? Can you please link to an input where this doesn't behave correctly? \$\endgroup\$ Commented May 2, 2018 at 14:47
  • \$\begingroup\$ Oh, no, you called mine out for the Does not seem to enforce equal line length too is all I was saying. \$\endgroup\$ Commented May 2, 2018 at 14:57
  • \$\begingroup\$ Doesn't seem to work for " \n " Try it online! \$\endgroup\$ Commented May 2, 2018 at 15:19
  • 1
    \$\begingroup\$ @Angs Try quoting it. It's apparently parsed as nothing if you put it like that. \$\endgroup\$ Commented May 2, 2018 at 15:21
0
\$\begingroup\$

Jelly, 15 bytes

Uses a method developed by Mnemonic in a (currently - due to an edge-case failure) deleted Pyth submission. (if it is now fixed up, go give some credit!)

ỴμL€Eȧt6ドルZUƊ4¡=

A monadic link accepting a list of characters which returns 1 or 0.

Try it online!

How?

ỴμL€Eȧt6ドルZUƊ4¡= - Link: list of characters
Ỵ - split at newlines (making a list of lists - the rows)
 μ - start a new monadic chain, call that ROWS
 L€ - length of €ach row in ROWS
 E - all equal? (an integer: 1 if so, otherwise 0)
 4¡ - repeat four times:
 Ɗ - last three links as a monad:
 t6ドル - trim spaces (6) from €ach row in current ROWS
 Z - transpose that result
 U - upend (reverse each new row)
 ȧ - logical AND (0 if L€E was 0 else the result of the repeated transform)
 = - equal to X? (the integer 0 is not equal to any listy of characters)
answered May 2, 2018 at 19:10
\$\endgroup\$
1
  • \$\begingroup\$ @Mnemonic - Jelly-fied :) \$\endgroup\$ Commented May 2, 2018 at 19:11
0
\$\begingroup\$

Japt, 22 bytes

Noncompeting answer: there's a known bug in Japt, where two-dimensional array rotations truncate the results. Due to that bug the code below only works on inputs that are square. If the bug wasn't present, though, the code below should work completely correctly.

e_ʶUÌÊéUeo4o)r_z)mx}U
e_ // Check if every line in the input array
 ʶUÌÊ // has the same length as the last item.
 é // Also,
 r_z)mx}U // check if rotating and trimming the input array
 o4o) // four times
 Ue // is equal to the input array.

Takes input as an array of strings. Using parentheses instead of spaces makes the rectangular code requirement quite easy.
Try it here.

answered May 2, 2018 at 23:53
\$\endgroup\$
0
\$\begingroup\$

Ruby 2.5+, 63 bytes

->a{!a.uniq(&:size)[1]&&a.none?(/^\s|\s$/)&&!(a[0]+a[-1])[?\s]}

Takes input as an array of strings. No test link, since the version on TIO (2.4) is too old for this one. Instead, here is a slightly longer (69 bytes) version for testing:

->a{!a.uniq(&:size)[1]&&a.none?{|l|l=~/^\s|\s$/}&&!(a[0]+a[-1])[?\s]}

Try it online!

The difference is that since 2.5 Ruby supports directly passing a Regex pattern to all?, any?, none? methods, which saves us a few bytes. The method itself is quite self-explanatory - we test:

  1. If there is only 1 unique line size
  2. If there are any spaces on line boundaries
  3. If there are spaces on the first and last lines.
answered May 3, 2018 at 8:29
\$\endgroup\$
0
\$\begingroup\$

C (gcc), 119 bytes

Takes input as a list (s) of n strings.

f(s,n,m,r,p)char**s,*p;{for(r=m=n;m--;r*=strlen(*s)==strlen(s[m])&(!p||m&&m^n-1&&p!=s[m]&&p[1]))p=strchr(s[m],32);n=r;}

Try it online!

answered May 3, 2018 at 10:37
\$\endgroup\$
0
\$\begingroup\$

C# (.NET Core), (削除) 145 (削除ここまで) 167 bytes

S[0].Length>1&&S[0].IndexOf
(" ") + S[ S.Count() - 1 ].
IndexOf(" ")<-1&Array.Find(
S,x=>x[0]==' '| x [x.Length
-1] == ' ' | S[0].Length
!=x.Length)==null?11>0:0>1;

Try it online!

S[0].Length>1& // And if the lenght of the first argument is more than 1 char
Array.Find( // Find a string in an array
 S, // The array which will be searched in
 x=> // For x as the current string from the array
 x.Length!=S[0].Length| // If the string lenght match not the first argument lenght
 x[0]==' '| // Or if the string begins with a spacer
 x[x.Length-1]==' ' // Or if the string ends with a spacer
)==null& // And if there was no string found which matched the conditions
S[0].IndexOf(" ")+S[S.Count()-1].IndexOf(" ")<-1 // And if the first and last string doesn't have a spacer
? // If all above is true do
1>0 // Return True
: // Else
0>1 // Return False
answered May 3, 2018 at 8:09
\$\endgroup\$
7
  • \$\begingroup\$ No spaces in the first line. \$\endgroup\$ Commented May 3, 2018 at 8:28
  • \$\begingroup\$ @FrownyFrog S[0].IndexOf(" ") is searching for a space in the first line and S[S.Count()-1].IndexOf(" ") is searching in the last line. If there is no space in the first and last line, it is -2 which is then true at -2 < -1. \$\endgroup\$ Commented May 3, 2018 at 9:53
  • 2
    \$\begingroup\$ I mean the challenge, your code has the same restriction, so you can’t have spaces in the first line. \$\endgroup\$ Commented May 3, 2018 at 10:41
  • 1
    \$\begingroup\$ Your code must return True when passed to your program. It’s an additional restriction in this challenge. \$\endgroup\$ Commented May 3, 2018 at 11:14
  • 1
    \$\begingroup\$ Let us continue this discussion in chat. \$\endgroup\$ Commented May 3, 2018 at 11:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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