Skip to main content
Bounty Awarded with 50 reputation awarded by FromTheStackAndBack
correct time complexity based on a comment
Source Link
Roman
  • 2.9k
  • 12
  • 23

Currently Digit calculates the form of a concrete digit based on the prop with the name digit. The calculation is inside a nested for-loop which means we havewith a time complexity of \$O(n^2)\$\$O(n)\$. But actually the calculation is redundant since the form of a digit is known on run-time so we can reduce the time complexity to \$O(1)\$(see final result, where we do not need to loop).

Currently Digit calculates the form of a concrete digit based on the prop with the name digit. The calculation is inside a nested for-loop which means we have a time complexity of \$O(n^2)\$. But actually the calculation is redundant since the form of a digit is known on run-time so we can reduce the time complexity to \$O(1)\$.

Currently Digit calculates the form of a concrete digit based on the prop with the name digit. The calculation is inside a nested for-loop with a time complexity of \$O(n)\$. But actually the calculation is redundant since the form of a digit is known on run-time so we can reduce the time complexity (see final result, where we do not need to loop).

Source Link
Roman
  • 2.9k
  • 12
  • 23

Consistency

Sometimes the word color or colour is used to describe the look of a Square. Because I'm not a native English speaker I looked it up on wikipedia:

color (American English), or colour (Commonwealth English)

The Algorithm

Currently Digit calculates the form of a concrete digit based on the prop with the name digit. The calculation is inside a nested for-loop which means we have a time complexity of \$O(n^2)\$. But actually the calculation is redundant since the form of a digit is known on run-time so we can reduce the time complexity to \$O(1)\$.

The form of a digit is already defined inside the array filled:

const filled = [
     [
       [1, 1, 1],
       [1, 0, 1],
       [1, 0, 1],
       [1, 0, 1],
       [1, 1, 1]
     ],
     /* .... */
]

But instead of define the form based on boolean values it is possible to insert the concrete React-Components to avoid the nested for-loops:

const filled = [
  [
    [<Square />, <Square />, <Square />, <br />],
    [<Square />, <Square colour="#FFFFFF"/>, <Square />, <br />],
    [<Square />, <Square colour="#FFFFFF"/>, <Square />, <br />],
    [<Square />, <Square colour="#FFFFFF"/>, <Square />, <br />],
    [<Square />, <Square />, <Square />]
  ]    
];

Expand to see the working example below

function getRandomColor() {
  const colours = ["#FF6663", "#94FF63", "#63FFEA", "#A763FF", "#F2FF63"];
  return colours[Math.floor(Math.random() * colours.length)];
}

class Square extends React.Component {
  render() {
    var squareStyle = {
      width: 25,
      height: 25,
      backgroundColor: this.props.colour || getRandomColor(),
      display: "inline-block"
    };

    return (
      <div style={squareStyle}>
      </div>
    );
  }
}

class Digit extends React.Component {
  render() {
    const divStyle = {
      display: "inline-block",
      lineHeight: 0,
      paddingLeft: "10px",
      paddingRight: "10px"
    };

    const filled = [
      [
        [<Square />, <Square />, <Square />, <br />],
        [<Square />, <Square colour="#FFFFFF"/>, <Square />, <br />],
        [<Square />, <Square colour="#FFFFFF"/>, <Square />, <br />],
        [<Square />, <Square colour="#FFFFFF"/>, <Square />, <br />],
        [<Square />, <Square />, <Square />]
      ]    
    ];

    return (
      <div style={divStyle}>
        {filled[this.props.digit]}
      </div>
    );
  }
}

ReactDOM.render(
  <div>
    <Digit digit={0} />
  </div>,
  document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Composition

Inside Reacts Documentation on composition is described how it can be used effectively.

Square

Currently the Square-Component can have random colors or a concrete color based on the prop. (In my opinion this is against the Single-Responsibility Principle ).

By extract the color behavior into separate components we can make the code more declarative:

const filled = [
      [
        [<RandomColoredSquare />, <RandomColoredSquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <EmptySquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <EmptySquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <EmptySquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <RandomColoredSquare />, <RandomColoredSquare />]
      ],
      /* ... */    
];

RandomColoredSquare and EmptySquare are specializations of Spuare:

const Square = ({color}) => 
  <div style={{
      width: 25,
      height: 25,
      backgroundColor: color,
      display: "inline-block"
    }}>
  </div>

const RandomColoredSquare = ({colors = ["#FF6663", "#94FF63", "#63FFEA", "#A763FF", "#F2FF63"]}) => 
  <Square color={colors[Math.floor(Math.random() * colors.length)]} />

const EmptySquare = _ => 
  <Square color="#FFF" />

I used the the destructuring assignment to make the code more readable. Instead of const Square = ({color}) I could have written const Square = (props) but then if have to query the color with props.color and now I can use directly the color.

Additionally I make use of the default parameters together with the destructuring assignment for ({colors = ["#FF6663", "#94FF63", "#63FFEA", "#A763FF", "#F2FF63"]}).

Expand to see the working example below

const Square = ({color}) => 
  <div style={{
      width: 25,
      height: 25,
      backgroundColor: color,
      display: "inline-block"
    }}>
  </div>

const RandomColoredSquare = ({colors = ["#FF6663", "#94FF63", "#63FFEA", "#A763FF", "#F2FF63"]}) => 
  <Square color={colors[Math.floor(Math.random() * colors.length)]} />

const EmptySquare = _ => 
  <Square color="#FFF" />

class Digit extends React.Component {
  render() {
    const divStyle = {
      display: "inline-block",
      lineHeight: 0,
      paddingLeft: "10px",
      paddingRight: "10px"
    };

    const filled = [
      [
        [<RandomColoredSquare />, <RandomColoredSquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <EmptySquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <EmptySquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <EmptySquare />, <RandomColoredSquare />, <br />],
        [<RandomColoredSquare />, <RandomColoredSquare />, <RandomColoredSquare />]
      ]    
    ];

    return (
      <div style={divStyle}>
        {filled[this.props.digit]}
      </div>
    );
  }
}

ReactDOM.render(
  <div>
    <Digit digit={0} />
  </div>,
  document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Digit

I think <Digit digit={0} /> is a less intuitiv API then if we could simply write <Zero /> which would make our code more declarative at the first glance. Additionally Digit violates the Open-Close Principle.

We can first describe the more generic component Digit and composite it the "special" components Zero, One and so on:

const Zero = _ => 
  <Digit>
    <RandomColoredSquare />
    <RandomColoredSquare />
    <RandomColoredSquare />
  
    <br />
  
    /* ... */
  </Digit>

const Digit = ({children}) => 
  <div style={{
      display: "inline-block",
      lineHeight: 0,
      paddingLeft: "10px",
      paddingRight: "10px"
    }}>
    {children}
  </div>

For Digit I used the Children in JSX where it is possible to pass Components inside a component instead vie props.

Expand to see the working example below

const Square = ({color}) => 
  <div style={{
      width: 25,
      height: 25,
      backgroundColor: color,
      display: "inline-block"
    }}>
  </div>

const RandomColoredSquare = ({colors = ["#FF6663", "#94FF63", "#63FFEA", "#A763FF", "#F2FF63"]}) => 
  <Square color={colors[Math.floor(Math.random() * colors.length)]} />

const EmptySquare = _ => 
  <Square color="#FFF" />

const Zero = _ => 
  <Digit>
    <RandomColoredSquare />
    <RandomColoredSquare />
    <RandomColoredSquare />
  
    <br />
  
    <RandomColoredSquare />
    <EmptySquare />
    <RandomColoredSquare />
  
    <br />
  
    <RandomColoredSquare />
    <EmptySquare />
    <RandomColoredSquare />
  
    <br />
  
    <RandomColoredSquare />
    <EmptySquare />
    <RandomColoredSquare />
  
    <br />
  
    <RandomColoredSquare />
    <RandomColoredSquare />
    <RandomColoredSquare />
  </Digit>

const Digit = ({children}) => 
  <div style={{
      display: "inline-block",
      lineHeight: 0,
      paddingLeft: "10px",
      paddingRight: "10px"
    }}>
    {children}
  </div>

ReactDOM.render(
  <div>
    <Zero />
  </div>,
  document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

But now we have this explicite <br />. We could introduce a new Component Row to make it implicit:

const Row = ({children}) =>
  <div>
    {children}
    <br />
  </div>

const Zero = _ => 
  <Digit>
    <Row>
      <RandomColoredSquare />
      <RandomColoredSquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      /* ... */
    </Row>
   
    /* ... */

  </Digit>

Expand to see the working example below

const Square = ({color}) => 
  <div style={{
      width: 25,
      height: 25,
      backgroundColor: color,
      display: "inline-block"
    }}>
  </div>

const RandomColoredSquare = ({colors = ["#FF6663", "#94FF63", "#63FFEA", "#A763FF", "#F2FF63"]}) => 
  <Square color={colors[Math.floor(Math.random() * colors.length)]} />

const EmptySquare = _ => 
  <Square color="#FFF" />

const Row = ({children}) =>
  <div>
    {children}
    <br />
  </div>

const Zero = _ => 
  <Digit>
    <Row>
      <RandomColoredSquare />
      <RandomColoredSquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <EmptySquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <EmptySquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <EmptySquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <RandomColoredSquare />
      <RandomColoredSquare />
    </Row>
  </Digit>

const Digit = ({children}) => 
  <div style={{
      display: "inline-block",
      lineHeight: 0,
      paddingLeft: "10px",
      paddingRight: "10px"
    }}>
    {children}
  </div>

ReactDOM.render(
  <div>
    <Zero />
  </div>,
  document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Final Result

The result is longer since we write the digits explicit instead of create them dynamic but with this way we have a much better time complexity and the code is easier to understand since we remove the computational logic and make the code more declarative.

const Square = ({color}) => 
  <div style={{
      width: 25,
      height: 25,
      backgroundColor: color,
      display: "inline-block"
    }}>
  </div>

const RandomColoredSquare = ({colors = ["#FF6663", "#94FF63", "#63FFEA", "#A763FF", "#F2FF63"]}) => 
  <Square color={colors[Math.floor(Math.random() * colors.length)]} />

const EmptySquare = _ => 
  <Square color="#FFF" />

const Row = ({children}) =>
  <div>
    {children}
    <br />
  </div>

const Zero = _ => 
  <Digit>
    <Row>
      <RandomColoredSquare />
      <RandomColoredSquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <EmptySquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <EmptySquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <EmptySquare />
      <RandomColoredSquare />
    </Row>
  
    <Row>
      <RandomColoredSquare />
      <RandomColoredSquare />
      <RandomColoredSquare />
    </Row>
  </Digit>

const Three = _ =>
  <Digit>
    <Row>
        <RandomColoredSquare />
        <RandomColoredSquare />
        <RandomColoredSquare />
    </Row>
  
    <Row>
        <EmptySquare />
        <EmptySquare />
        <RandomColoredSquare />
    </Row>
  
    <Row>
        <EmptySquare />
        <RandomColoredSquare />
        <RandomColoredSquare />
    </Row>
  
    <Row>
        <EmptySquare />
        <EmptySquare />
        <RandomColoredSquare />
    </Row>
  
    <Row>
        <RandomColoredSquare />
        <RandomColoredSquare />
        <RandomColoredSquare />
    </Row>
  </Digit>

const Digit = ({children}) => 
  <div style={{
      display: "inline-block",
      lineHeight: 0,
      paddingLeft: "10px",
      paddingRight: "10px"
    }}>
    {children}
  </div>

ReactDOM.render(
  <div>
    <Zero />
    <Three />
    <Zero />
  </div>,
  document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>