Truncate values

Script in #29 was working:

const trunc = (num, places = 0) => {
  if (places <= 0) {
    return String(Math.trunc(num.split(' ')[0]))
  }

  const regex = new RegExp(`\\d+\\.\\d{1,${places}}`)
  const match = num.match(regex)

  return (match !== null) ? match[0] : null
}

const truncGroups = [
  {
    groupRx: /8[4-6][0-9].*/, 
    truncate: (code) => {
      const [num, letters] = code.split(/ ([A-Z]{2})/)
      const truncNumber = trunc(num, 1)

      return (letters)
        ? `${truncNumber} ${letters}`
        : `${truncNumber}`
    }
  },

  {
    groupRx: /80[1-7]/,
    truncate: (code) => trunc(code),
  },

  {
    groupRx: /839.3[16]*/,
    truncate: (code) => trunc(code, 2)
  }
]

const formatCode = (code) => {
  for (const { groupRx, truncate } of truncGroups) {
    if (groupRx.test(code)) {
      return truncate(code)
    }
  }
  return code
}

// Update value of <input name='collocazione'>
const collocazione = document.querySelector('#collocazione');

if (collocazione) {
  collocazione.value = formatCode(collocazione.value);
}

The goal posts are moving quite a bit, can you confirm that these are all the desired results?

/* match 840-869 */
847.467 4        → 847.4 Pass
850.767 456      → 850.7 Pass
869.999 3        → 869.9 Pass
861.39 CU        → CU 861.3 Pass
863.456 CL       → CL 863.4 Pass
861.444 05 AR    → AR 861.4 Pass
869.347 5 BR     → BR 869.3 Pass

/* match 801-807 */
802.556 667567   → 802 Pass
805.875 4        → 805 Pass

/* match 839.31 or 839.36 */
839.315 6        → 839.31 Pass
839.366 87       → 839.36 Pass
111.aaa 1        → 11.aaa Pass

/* match 823 or 843 numbers */
823.556 3 IE     → 823.9 Pass
823.358 44 AU    → 823.9 Pass
823.45 ZA        → 823.9 Pass
823.234 6 NZ     → 823.9 Pass
823.34 KE        → 823.9 Pass
823.35 ZW        → 823.9 Pass
823.347 3 LK     → 823.9 Pass
843.914 45653    → 843.9 Pass
843.356 3 BE     → 843.9 Pass
843.567 6 CA     → 843.9 Pass
843.246 8554 CH  → 843.9 Pass
843.345 6 CI     → 843.9 Pass

If so, you should be able to copy and paste this into your Autofill.

const trunc = (num, places = 0) => {
  if (places <= 0) {
    return String(Math.trunc(num.split(' ')[0]))
  }

  const regex = new RegExp(`\\d+\\.\\d{1,${places}}`)
  const match = num.match(regex)

  return (match !== null) ? match[0] : null
}

const truncGroups = [
  // match 823 or 843 numbers
  // truncate to number with a decimal of .9
  {
    groupRx: /\b823\b|\b843\b/,
    truncate: (code) => code.substring(0, 3) + ".9"
  },
  
  // match 840-869
  // truncate to 1 decimal place, with optional letters
  // Note: this will match 843 numbers too. Hence the order!
  {
    groupRx: /\b8[4-6][0-9]\b/, 
    truncate: (code) => {
      const [num, letters] = code.split(/ ([A-Z]{2})/)
      const truncNumber = trunc(num, 1)

      return (letters)
        ? `${letters} ${truncNumber}`
        : `${truncNumber}`
    }
  },
  
  // match 801-807
  // truncate to an integer
  {
    groupRx: /\b80[1-7]\b/,
    truncate: (code) => trunc(code),
  },
  
  // match 839.31 or 839.36
  // truncate to 2 decimal places
  {
    groupRx: /839\.3[16]/,
    truncate: (code) => trunc(code, 2)
  }
]

const formatCode = (code) => {
  for (const { groupRx, truncate } of truncGroups) {
    if (groupRx.test(code)) {
      return truncate(code)
    }
  }
  return code
}

// Update value of <input name='collocazione'>
const collocazione = document.querySelector('#collocazione');

if (collocazione) {
  collocazione.value = formatCode(collocazione.value);
}

Following Paul’s tests: (Note: not to be copied and pasted into Autofill)

const tests = [
  // group matching 8[4-6][0-9]
  { code: '847.467 4', expected: '847.4' },
  { code: '850.767 456', expected: '850.7' },
  { code: '869.999 3', expected: '869.9' },
  { code: '861.39 CU', expected: 'CU 861.3' },
  { code: '863.456 CL', expected: 'CL 863.4' },
  { code: '861.444 05 AR', expected: 'AR 861.4' },
  { code: '869.347 5 BR', expected: 'BR 869.3' },

  // group matching 80[1-7]
  { code: '802.556 667567', expected: '802' },
  { code: '805.875 4', expected: '805' },

  // group matching 839.3[1,6]
  { code: '839.315 6', expected: '839.31' },
  { code: '839.366 87', expected: '839.36' },
  { code: '111.aaa', expected: '111.aaa' },
  
  // group matching 823|843
  { code: "823.556 3 IE", expected: "823.9" },
  { code: "823.358 44 AU", expected: "823.9" },
  { code: "823.45 ZA", expected: "823.9" },
  { code: "823.234 6 NZ", expected: "823.9" },
  { code: "823.34 KE", expected: "823.9" },
  { code: "823.35 ZW", expected: "823.9" },
  { code: "823.347 3 LK", expected: "823.9" },
  { code: "843.914 45653", expected: "843.9" },
  { code: "843.356 3 BE", expected: "843.9" },
  { code: "843.567 6 CA", expected: "843.9" },
  { code: "843.246 8554 CH", expected: "843.9" },
  { code: "843.345 6 CI", expected: "843.9" }
]

tests.forEach(({ code, expected }) => {
  const converted = formatCode(code)
  console.log(
    code, 
    converted, 
    converted === expected ? 'Pass' : 'Fail'
  )
})

In post #37 they moved even further to have no letters, and the decimal value capped always at .9

From the working code which was:

const formatCode = (code) => {
  for (const { groupRx, truncate } of truncGroups) {
    if (groupRx.test(code)) {
      return truncate(code)
    }
  }
  return code
}

That can be updated to achieve the desired results by updating all of the above code to just be:

const formatCode = (code) => code.substring(0, 3) + ".9";

After which you can then remove the unused trunc and truncGroups code.

That was for numbers starting with 823 and 843, no? In which case the truncGroups still apply. I have listed them out in post 51. Could be wrong, we’ll see.

lawrencedaniello ? Was the 823 and 843 only for example purposes, or were they specific?

Is it this?

  • 861.39 CU => CU 861.3
  • 823.45 ZA => 823.9

Or is it this?

  • 861.39 CU => 861.9
  • 823.45 ZA => 823.9

rpg_digital (post #51), let’s simplify the first group, considering only values starting with 86[1-9] and two letters, as below:

/* match 86[1-9] XX /
/
match 840-869 */
847.467 4 → 847.4 Pass
850.767 456 → 850.7 Pass
869.999 3 → 869.9 Pass

861.39 CU → CU 861.3 Pass //@Paul_Wilkins (post #54): not 861.9
863.456 CL → CL 863.4 Pass
861.444 05 AR → AR 861.4 Pass
869.347 5 BR → BR 869.3 Pass

/* match 801-807 */
802.556 667567 → 802 Pass
805.875 4 → 805 Pass

/* match 839.31 or 839.36 */
839.315 6 → 839.31 Pass
839.366 87 → 839.36 Pass
111.aaa 1 → 11.aaa Pass //?? What’s this?

/* match 823 or 843 numbers */
823.556 3 IE → 823.9 Pass
823.358 44 AU → 823.9 Pass
823.45 ZA → 823.9 Pass
823.234 6 NZ → 823.9 Pass
823.34 KE → 823.9 Pass
823.35 ZW → 823.9 Pass
823.347 3 LK → 823.9 Pass
843.914 45653 → 843.9 Pass
843.356 3 BE → 843.9 Pass
843.567 6 CA → 843.9 Pass
843.246 8554 CH → 843.9 Pass
843.345 6 CI → 843.9 Pass

1 Like

See below Lawrence.

const trunc = (num, places = 0) => {
  if (places <= 0) {
    return String(Math.trunc(num.split(' ')[0]))
  }

  const regex = new RegExp(`\\d+\\.\\d{1,${places}}`)
  const match = num.match(regex)

  return (match !== null) ? match[0] : null
}

const truncGroups = [
  // match 823 or 843 numbers
  // truncate to number with a decimal of .9
  {
    groupRx: /\b823\b|\b843\b/,
    truncate: (code) => code.substring(0, 3) + ".9"
  },

  // match number starting with 860-869 + region letters
  // truncate to 1 decimal place
  {
    groupRx: /\b86[1-9]\b.* [A-Z]{2}$/, 
    truncate: (code) => {
      const [num, letters] = code.split(/ ([A-Z]{2})/)
      const truncNumber = trunc(num, 1)

      return `${letters} ${truncNumber}`
    }
  },
  
  // match number starting with 801-807
  // truncate to an integer
  {
    groupRx: /\b80[1-7]\b/,
    truncate: (code) => trunc(code),
  },
  
  // match 839.31 or 839.36
  // truncate to 2 decimal places
  {
    groupRx: /839\.3[16]/,
    truncate: (code) => trunc(code, 2)
  }
]

const formatCode = (code) => {
  for (const { groupRx, truncate } of truncGroups) {
    if (groupRx.test(code)) {
      return truncate(code)
    }
  }
  return code
}

// Update value of <input name='collocazione'>
const collocazione = document.querySelector('#collocazione');

if (collocazione) {
  collocazione.value = formatCode(collocazione.value);
}

Thanks rpg_digital, also for your effort to understand, your script works really well!! Thanks again!

1 Like

Hi, rpg_digital, is it possible to perfect last group? Values starting with 8[2,4]3 and two letters should be converted to 8[2,4]3.9 as above, but when there aren’t letters should be truncated to first decimal. Thank you again!

So to confirm, we currently have:

823.??? 3 IE → 823.9
843.??? 3 IE → 843.9

And you’re wanting the following to also occur:

823.456 3 → 823.4
843.567 → 843.5

Paul_Wilkins
823.??? IE → 823.9 (not necessarily 3 as last decimal input)
843.??? IE → 843.9

823.456 3 → 823.4
843.567 → 843.5

I kind of feel like this thread has been a great big exercise in “Why dont you just use a Select box instead of text input”.

Because a select box would be far too long ???

Perhaps a chain of select boxes could work.

Searchable select from something like Select2 would do it as well? (Yes i know, we’re all avoiding the idea of loading a framework. Bootstrap does one too i think.)

1 Like

“Collocazione” field is autofilled by the proprietary software, and I would like its value to be automatically truncated.
rpg_digital’s script is fine, but has to be simply perfected in the first group.

@lawrencedaniello,

Not ignoring you :slight_smile: Just a bit busy right now.

Can I ask what have you tried yourself? You could create a codepen and based on the answers supplied by the members here, try out a few things.

I think that would be worthwhile.

No problem, thanks! I have focused on these two groups but I can’t go on:

{
    groupRx: /\b823\b|\b843\b/,
    truncate: (code) => {
      const [num, letters] = code.split(/ ([A-Z]{2})/)
      const truncNumber = trunc(num, 1)

      return (letters)
        ? `${letters} ${truncNumber}`
        : `${truncNumber}`
    }
  },
{
    groupRx: /\b823\b|\b843\b/,
    truncate: (code) => code.substring(0, 3) + ".9"
  },

You need to update the above groupRx for the top group so that it matches “823.??? IE” with the letters at the end, and the bottom groupRx so that it applies when there are no letters at the end.

Different regular expressions can be tested at https://regex101.com/
I’ve tested with the following test strings:

823.556 3 IE
823.345 IE
843.556 3 IE
843.345 IE
823.456 3
843.567

You use all of those strings at the same time for testing, but just ensure that you’ve set flags for Global and Multiline. That way your regex will show matches all across everything in that list.

The following regex matches 823 and 843 followed afterwards by letters
/(823|843).*[A-Z]+/

The opposite of that isn’t as easy to achieve, but seems to be achieved by insisting that everything after the 823 or 843 is a digit, fullstop, or a space, until the end of the line (that’s the $)
/(823|843)[0-9. ]+$/

1 Like

There are though other situations that result in false matches, such as:

123.823 3 IE

To help resolve those, seems to require using ^ and $ to indicate the start and end of the string.
/^(823|843).*[A-Z]+$/

That regex matches:

  • 823.556 3 IE
  • 823.345 IE
  • 843.556 3 IE
  • 843.345 IE

And that regex (correctly) doesn’t match:

  • 123.823 3 IE

Similarly with the regex to match 823 or 843 with no letters on the line:
/^(823|843)[0-9. ]+$/

That matches the following:

  • 823.456 3
  • 843.567

And correctly doesn’t match:

  • 123.823 3

Thank you, Paul_Wilkins, the values I tested seem working with your tip. I’m wondering why all groups work, except
/891\.[7-8]/
It should be truncated at second decimal, but doesn’t. It works though if I delete all other groups. I tried with two different extensions, same result.

const trunc = (num, places = 0) => {
  if (places <= 0) {
    return String(Math.trunc(num.split(' ')[0]))
  }

  const regex = new RegExp(`\\d+\\.\\d{1,${places}}`)
  const match = num.match(regex)

  return (match !== null) ? match[0] : null
}

const truncGroups = [
// truncate to an integer
  {
    groupRx: /\b80[1-7]\b/,
    truncate: (code) => trunc(code)
  },
{
    groupRx: /809\.9/,
    truncate: (code) => trunc(code, 1)
  },

{
    groupRx: /\b81[0-9]\b/,
    truncate: (code) => trunc(code, 1)
  },

{
    groupRx: /^(8[2,4][0-9]).*[A-Z]+/,
     truncate: (code) => code.substring(0, 3) + ".9"
    }, 

{
    groupRx: /(8[2,4][0-9])[0-9. ]+$/,
    truncate: (code) => trunc(code, 1)
  },
  
{
    groupRx: /\b83[0-8]\b/,
    truncate: (code) => trunc(code, 1)
  },
  
  // match 839.31 or 839.36
  // truncate to 2 decimal places
  {
    groupRx: /839\.3[1,6]/,
    truncate: (code) => trunc(code, 2)
  },
{
    groupRx: /839\.693/,
    truncate: (code) => trunc(code, 3)
  },
{
    groupRx: /839\.7/,
    truncate: (code) => trunc(code, 1)
  },

{
    groupRx: /85[0-3]\b/,
    truncate: (code) => trunc(code, 1)
  },
{
    groupRx: /\b85[4-9]\b/,
    truncate: (code) => trunc(code)
  },

// match number starting with 860-869 + region letters
  // truncate to 1 decimal place
  {
    groupRx: /\b86[0-9]\b/, 
    truncate: (code) => {
      const [num, letters] = code.split(/ ([A-Z]{2})/);
      const truncNumber = trunc(num, 1)

      return (letters)
        ? `${letters} ${truncNumber}`
        : `${truncNumber}`;
    }
    },
{
    groupRx: /8[7-8][0-8]\b/,
    truncate: (code) => trunc(code)
  },
{
    groupRx: /889\.3[3-4]/,
    truncate: (code) => trunc(code, 2)
  },
{
    groupRx: /891\.[7-8]/,
    truncate: (code) => trunc(code, 2)
  }
];

const formatCode = (code) => {
  for (const { groupRx, truncate } of truncGroups) {
    if (groupRx.test(code)) {
      return truncate(code);
    }
  }
  return code;
};

// Update value of <input name='collocazione'>
const collocazione = document.querySelector('#collocazione');

if (collocazione) {
  collocazione.value = formatCode(collocazione.value);
}