0

I'm very new to JavaScript and am trying to learn it right now. I have a few exercises that I just want to solve and where I get stuck. It's about sorting. Maybe it's a fundamental problem of understanding on my part. I have an array that I want to sort. The array has a sorting sequence process (Carddeck):

let RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
let SUITS = ['A', 'B', 'C', 'D'];

the array to sort:

let arrayToSort = ['10B', '9D', '9C', '9A', 'AA']
            // => ['9A', '9C', '9D', '10B', 'AA'] 

I thought that I would need a compareFunction(a, b) for indexOf RANKS and SUITS.

.sort(function (a, b) 
   {
   if ( a.suitIndex < b.suitIndex 
      || ( a.suitIndex == b.suitIndex && a.rankIndex <  b.rankIndex )
      ) {
      return -1; 
   }
   return 1; 
   });

Then I tried to find out the index value.

let RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
let SUITS = ['A', 'B', 'C', 'D'];

let arrayToSort = ['10B', '9D', '9C', '9A', 'AA']

let card1 = arrayToSort.slice(0, 1).join()
let card2 = arrayToSort.slice(1, 2).join()
let card3 = arrayToSort.slice(2, 3).join()
let card4 = arrayToSort.slice(3, 4).join()
let card5 = arrayToSort.slice(4, 5).join()

console.log( card1, RANKS.indexOf(card1.slice(0, -1)), SUITS.indexOf(card1.charAt(card1.length - 1)))
console.log( card2, RANKS.indexOf(card2.slice(0, -1)), SUITS.indexOf(card2.charAt(card2.length - 1)))
console.log( card3, RANKS.indexOf(card3.slice(0, -1)), SUITS.indexOf(card3.charAt(card3.length - 1)))
console.log( card4, RANKS.indexOf(card4.slice(0, -1)), SUITS.indexOf(card4.charAt(card4.length - 1)))
console.log( card5, RANKS.indexOf(card5.slice(0, -1)), SUITS.indexOf(card5.charAt(card5.length - 1)))
.as-console-wrapper { max-height: 100% !important; top: 0; }

At this point I get stuck. I don't want a solution at first. I want to understand where my fault is and which way I should rather look. I would be happy to receive a tip. Thank you.

Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
C3PO
  • 13
  • 6
  • 1
    I don't see you ever defining a `suitIndex` (or `rankIndex`) property on the items to sort, so that's one problem to fix – CertainPerformance Mar 19 '21 at 17:30
  • Does this answer your question? [Javascript : natural sort of alphanumerical strings](https://stackoverflow.com/questions/2802341/javascript-natural-sort-of-alphanumerical-strings) – ponury-kostek Mar 19 '21 at 17:40
  • @CertainPerformance I know that I don't have a suitIndex or rankIndex yet. At first I only looked at the individual parts and painted them up in order to find a pictorial solution. I thought that I can compare this function. But I still have no idea how to define suitIndex. It was more the question of whether it makes sense my approach? – C3PO Mar 19 '21 at 18:40
  • I updated my answer (below) by adding the explanation of the error present in your sort (with a demo) – Mister Jojo Mar 20 '21 at 21:42

2 Answers2

3

Your main problem is that you choose a wrong data structure (strings) to represent cards. A card is an object with (at least) two attributes "rank" and "suit" and should be defined as such. For example, instead of something like "KS" it would be better to use

{ rank: RANK_KING, suit: SUIT_SPADES }

where RANK_KING and SUIT_SPADES are numeric constants, e.g.:

const RANK_KING = 13;
const SUIT_SPADES = 2;

With such data structure, sorting and other operations, like computing hands, become very easy to program, for example, to sort by rank and then by suit:

deck.sort((a, b) => a.rank - b.rank || a.suit - b.suit)
Dharman
  • 30,962
  • 25
  • 85
  • 135
georg
  • 211,518
  • 52
  • 313
  • 390
  • RANKS and SUITS are specified as const in the question. And arrayToSort which should be a function. – C3PO Mar 19 '21 at 19:13
0

You could map your string values to a Card class. This class can hold a reference to a Rank and a Suit "enum"-like Object. The object can implement a way to compare two cards and serialize the data back into strings.

const main = () => {
  const cardStrings = ["10B", "9D", "9C", "9A", "AA"];
  const cardObjects = cardStrings.map(str => new Card(str));

  cardObjects.sort((cardA, cardB) => Card.compare(cardA, cardB));

  // Expected: ["9A", "9C", "9D", "10B", "AA"] 
  console.log(JSON.stringify(cardObjects.map(card => card.toString())));
};

const Rank = {
  '2'  : { symbol:  '2', ordinal:  0, text: 'Two'   },
  '3'  : { symbol:  '3', ordinal:  1, text: 'Three' },
  '4'  : { symbol:  '4', ordinal:  2, text: 'Four'  },
  '5'  : { symbol:  '5', ordinal:  3, text: 'Five'  },
  '6'  : { symbol:  '6', ordinal:  4, text: 'Six'   },
  '7'  : { symbol:  '7', ordinal:  5, text: 'Seven' },
  '8'  : { symbol:  '8', ordinal:  6, text: 'Eight' },
  '9'  : { symbol:  '9', ordinal:  7, text: 'Nine'  },
  '10' : { symbol: '10', ordinal:  8, text: 'Ten'   },
  'J'  : { symbol:  'J', ordinal:  9, text: 'Jack'  },
  'Q'  : { symbol:  'Q', ordinal: 10, text: 'Queen' },
  'K'  : { symbol:  'K', ordinal: 11, text: 'King'  },
  'A'  : { symbol:  'A', ordinal: 12, text: 'Ace'   }
};

const Suit = {
  'A'  : { symbol: 'A', ordinal: 0, text: 'A' },
  'B'  : { symbol: 'B', ordinal: 1, text: 'B' },
  'C'  : { symbol: 'C', ordinal: 2, text: 'C' },
  'D'  : { symbol: 'D', ordinal: 3, text: 'D' },
};

class Card {
  constructor(str) {
    const [ , rank, suit ] = str.match(/^(\w{1,2})([A-D])$/);
    this.rank = Rank[rank];
    this.suit = Suit[suit];
  }
  compareWith(other) {
    return Card.compare(this, other);
  }
  toString() {
    return `${this.rank.symbol}${this.suit.symbol}`;
  }
  static compare(cardA, cardB) {
    return cardA.rank.ordinal - cardB.rank.ordinal ||
      cardA.suit.ordinal - cardB.suit.ordinal
  }
}

main();
.as-console-wrapper { top: 0; max-height: 100% !important; }

Original response

You can sort the cards by using a regular expression to match the rank and suit. You can then use the matched ranks and suits to find the index to use in the difference calculation.

const RANKS = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
const SUITS = ["A", "B", "C", "D"];

const sortCards = cards => cards.sort((a, b) => {
  const [ , aRank, aSuit ] = a.match(/^(\w{1,2})([A-D])$/);
  const [ , bRank, bSuit ] = b.match(/^(\w{1,2})([A-D])$/);
  const aRankIndex = RANKS.indexOf(aRank), bRankIndex = RANKS.indexOf(bRank);
  const diff = aRankIndex - bRankIndex;
  if (diff !== 0) return diff;
  const aSuitIndex = SUITS.indexOf(aSuit), bSuitIndex = SUITS.indexOf(bSuit);
  return aSuitIndex - bSuitIndex;
});

const cardsToSort = ["10B", "9D", "9C", "9A", "AA"];

// Expected: ["9A", "9C", "9D", "10B", "AA"] 
console.log(JSON.stringify(sortCards(cardsToSort)));
.as-console-wrapper { top: 0; max-height: 100% !important; }

Here is a simpler version with a compare function:

const
  RANKS   = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"],
  SUITS   = ["A", "B", "C", "D"],
  PATTERN = /^(\w{1,2})([A-D])$/;

const
  rankIndexLookup = RANKS.reduce((o, v, i) => ({ ...o, [v]: i }), {}),
  suitIndexLookup = SUITS.reduce((o, v, i) => ({ ...o, [v]: i }), {});

const compareCards = ([aRank, aSuit], [bRank, bSuit]) =>
  rankIndexLookup[aRank] - rankIndexLookup[bRank] ||
  suitIndexLookup[aSuit] - suitIndexLookup[bSuit];

const sortCards = cards => cards.sort((a, b) => compareCards(
  a.match(PATTERN).slice(1), b.match(PATTERN).slice(1)));

const cardsToSort = ["10B", "9D", "9C", "9A", "AA"];

console.log(JSON.stringify(sortCards(cardsToSort)));
.as-console-wrapper { top: 0; max-height: 100% !important; }
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • Not only doesn't this answer the question, it also demonstrates poor practices. – Ouroborus Mar 19 '21 at 18:06
  • @Ouroborus but it does solve the sorting issue, given the provided input and expected output. – Mr. Polywhirl Mar 19 '21 at 18:08
  • The question asked about where the asker was going wrong in their own solution and requested that responses not provide a specific solution. Your answer fails on both points. – Ouroborus Mar 19 '21 at 18:13
  • @Ouroborus too bad I explained how to solve the issue in my opening statement. – Mr. Polywhirl Mar 19 '21 at 18:17
  • No, you provided an alternate solution. You make no attempt to guide a new programmer in correcting their existing solution as they asked. Take a look at the [upvoted answer](https://stackoverflow.com/a/66713299/367865). – Ouroborus Mar 19 '21 at 18:22
  • @Ouroborus ok, I took your advice and improved my response. – Mr. Polywhirl Mar 19 '21 at 18:40