ค้นหาองค์ประกอบ min / max ของ Array ใน JavaScript


779

ฉันจะได้รับองค์ประกอบ min หรือ max ของ JavaScript Array ได้อย่างง่ายดายได้อย่างไร?

ตัวอย่าง Psuedocode:

let array = [100, 0, 50]

array.min() //=> 0
array.max() //=> 100

93
หมายเหตุ:ด้วย ECMAScript 6 คุณสามารถใช้ใหม่ผู้ประกอบการแพร่กระจาย (สามจุด: ...) ด้วยเช่นนี้Math.max() Math.max(...[2, 5, 16, 1])ดูคำตอบของฉันทำจากเอกสาร MDN
totymedli

1
พิจารณาการทำเครื่องหมายคำตอบตามที่ยอมรับ
อเล็กซ์

นี่เป็นมาตรฐานสำหรับการเปรียบเทียบความเร็วของวิธีการทั่วไปที่ทำได้: jsben.ch/#/1QuTg
EscapeNetscape

es6 นั้นยอดเยี่ยมโดยทั่วไป! :)
datdinhquoc

โดยไม่ต้อง ES6 Math.max.apply(null, [2,5,16,1])
Tomer

คำตอบ:


828

วิธีการเพิ่มวัตถุ Array ในตัวให้ใช้Math.max/ Math.minแทน:

Array.prototype.max = function() {
  return Math.max.apply(null, this);
};

Array.prototype.min = function() {
  return Math.min.apply(null, this);
};

นี่คือJSFiddle

ขยายตัว ins สามารถทำให้เกิดการชนกับห้องสมุดอื่น ๆ (เห็นบางส่วน) ดังนั้นคุณอาจจะสะดวกสบายมากขึ้นมีเพียงapply'ไอเอ็นจีMath.xxx()ไปยังอาร์เรย์ของคุณโดยตรง:

var min = Math.min.apply(null, arr),
    max = Math.max.apply(null, arr);

อีกทางหนึ่งสมมติว่าเบราว์เซอร์ของคุณรองรับ ECMAScript 6 คุณสามารถใช้ตัวดำเนินการสเปรดซึ่งทำหน้าที่คล้ายกับapplyวิธีการ:

var min = Math.min( ...arr ),
    max = Math.max( ...arr );

8
@HankH: ผ่านnullหรือMathหรือ{}อะไรก็ตามที่มีapply()หรือcall()ไม่มีผลต่อ Math.maxไม่ควรและไม่ควรอ้างอิงthisภายใน
Roatin Marth

31
ในฐานะโปรแกรมเมอร์ C # ฉันต้องการคำถามที่พิมพ์อย่างมาก
ChaosPandion

7
เพิ่งแบ่งปันข้อผิดพลาด jQuery ที่ฉันทำกับรหัสด้านบนซึ่งใช้เวลานานในการดีบัก อาร์เรย์ jquery ทำงานได้ดีกับทุกสิ่งยกเว้น iPad ฉันต้องแปลงอาร์เรย์เป็นอาร์เรย์ดั้งเดิมที่แท้จริงเพื่อให้ทำงานได้ ส่งผลกระทบต่ออุปกรณ์เครื่องเดียวด้วยเหตุผลบางอย่างMath.max.apply(null, $.makeArray(array));
Forrest

11
ฉันลดระดับลงเนื่องจากวิธีการที่เสนอใช้หน่วยความจำ O (n) ในกรอบสแต็กและเป็นผลให้เกิดปัญหาในอาร์เรย์ขนาดใหญ่ ในกรณีของฉันเพียงประมาณ 130000 หมายเลขก็เพียงพอที่จะพัง nodejs
Alexey Timanovsky

14
อย่าเพิ่มต้นแบบในตัวเช่นนี้ มันไม่ใช่แค่ความขัดแย้งกับห้องสมุดอื่น ๆ มันยังเกี่ยวกับความเป็นไปได้ที่เบราว์เซอร์จะจัดเตรียม.maxหรือ.minวิธีการในอนาคต สถานการณ์ที่สมจริงสมบูรณ์แบบ: คุณใช้คำตอบนี้ ในปี 2016 หรือ ES7 ES8 ข้อมูลจำเพาะและArray.max Array.minต่างจากรุ่นนี้พวกมันทำงานบนสตริง เพื่อนร่วมงานในอนาคตของคุณพยายามรับสตริงตัวอักษรล่าสุดในอาร์เรย์ด้วย.max()วิธีการเนทีฟที่ได้รับการNaNบันทึกไว้อย่างดี แต่ได้มาอย่างลึกลับ ชั่วโมงต่อมาเธอพบรหัสนี้เรียกใช้git blameและสาปแช่งชื่อของคุณ
Mark Amery

362
var max_of_array = Math.max.apply(Math, array);

สำหรับดูการอภิปรายเต็ม: http://aaroncrane.co.uk/2008/11/javascript_max_api/


13
ความแตกต่างระหว่างMath.max.apply(Math, array)และMath.max.apply(null, array)คืออะไร? บล็อกบอกว่า "... คุณต้องพูดซ้ำ ๆ ซ้ำอีกครั้งว่าmaxเป็นของMath... " แต่ดูเหมือนว่าฉันไม่จำเป็นต้องทำเช่นนั้น (โดยตั้งค่าอาร์กิวเมนต์แรกapplyเป็นnull)
ziyuang

9
@ziyuang เมื่อคุณเรียกมันเหมือนMath.max(a,b), Mathจะถูกส่งเป็นค่าดังนั้นจึงอาจจะทำให้ความรู้สึกที่จะทำเช่นเดียวกันกับเมื่อโทรthis applyแต่Math.maxไม่ใช้thisค่าเพื่อให้คุณสามารถส่งผ่านค่าที่คุณต้องการ
Oriol

199

สำหรับอาร์เรย์ขนาดใหญ่ (องค์ประกอบ ~ 10⁷) Math.minและMath.maxทั้งคู่สร้างข้อผิดพลาดต่อไปนี้ใน Node.js

RangeError: เกินขนาดสแต็กการโทรสูงสุด

โซลูชันที่มีประสิทธิภาพมากขึ้นคือการไม่เพิ่มอิลิเมนต์ทุกตัวใน call stack แต่แทนที่จะส่งผ่านอาร์เรย์:

function arrayMin(arr) {
  return arr.reduce(function (p, v) {
    return ( p < v ? p : v );
  });
}

function arrayMax(arr) {
  return arr.reduce(function (p, v) {
    return ( p > v ? p : v );
  });
}

หากคุณกังวลเกี่ยวกับความเร็วรหัสต่อไปนี้คือ ~ 3 ครั้งเร็วขึ้นแล้วก็Math.max.applyคือในคอมพิวเตอร์ของฉัน ดูhttp://jsperf.com/min-and-max-in-array/2

function arrayMin(arr) {
  var len = arr.length, min = Infinity;
  while (len--) {
    if (arr[len] < min) {
      min = arr[len];
    }
  }
  return min;
};

function arrayMax(arr) {
  var len = arr.length, max = -Infinity;
  while (len--) {
    if (arr[len] > max) {
      max = arr[len];
    }
  }
  return max;
};

หากอาร์เรย์ของคุณมีสตริงแทนตัวเลขคุณจะต้องบังคับให้เป็นตัวเลข โค้ดด้านล่างทำเช่นนั้น แต่จะทำให้โค้ดช้าลง ~ 10 ครั้งในเครื่องของฉัน ดูhttp://jsperf.com/min-and-max-in-array/3

function arrayMin(arr) {
  var len = arr.length, min = Infinity;
  while (len--) {
    if (Number(arr[len]) < min) {
      min = Number(arr[len]);
    }
  }
  return min;
};

function arrayMax(arr) {
  var len = arr.length, max = -Infinity;
  while (len--) {
    if (Number(arr[len]) > max) {
      max = Number(arr[len]);
    }
  }
  return max;
};

กำหนดminและmaxองค์ประกอบสุดท้ายและลดการวนซ้ำตาม 1 ( while(--len));)
Venugopal

@Venugopal คุณต้องตรวจสอบพิเศษเพื่อดูว่าอาร์เรย์ว่างเปล่าและส่งกลับค่า +/- Infinity
Linus Unnebäck

2
แปลก ... ฉันไปที่เว็บไซต์ที่เชื่อมโยง ... และการทดสอบใน Firefox 51.0.0 / Mac OS X 10.12.0 วิธีการลดแบบช้ากว่าการใช้ลูปถึง 30% ... ผลลัพธ์ที่แตกต่างกันมาก
Pierpaolo Cira

2
very different results คุณทำสิ่งนี้ 5 ปีต่อมา)
АлексейЛещук

1
ในปี 2019การreduceแก้ปัญหาช้าที่สุด แม้ว่าคุณจะทำงานกับอาร์เรย์ที่มีนับล้านขององค์ประกอบมันเป็นดีกว่าการใช้มาตรฐานสำหรับวง ดูคำตอบของฉันมากขึ้น
totymedli

152

การใช้สเปรดโอเปอเรเตอร์ (ES6)

Math.max(...array);  // the same with "min" => Math.min(...array);


8
โซลูชันนี้มีให้โดยคำตอบอื่น ๆ อีกหลาย คำตอบ
totymedli

15
Math.max (... []) = -Infinity ฮ่าฮ่าฮ่า😂😂😂
David Portabella

@DavidPortabella ไม่แน่ใจว่าทำไมมันถึงตลก นั่นเป็นวิธีการทำงานตามข้อกำหนด :If no arguments are given, the result is -∞.
Patrick Roberts

3
ใช่ฉันหมายความว่าข้อกำหนดของจาวาสคริปต์นั้นน่ากลัว ดูเหมือนว่าจะไม่สามารถคำนวณจำนวนต่ำสุดของตัวเลขไม่ได้ ในภาษาการเขียนโปรแกรมที่รุนแรงกว่าอื่น ๆ เช่น Scala การขอให้นาทีของอาร์เรย์ว่างเปล่าเกิดข้อยกเว้น
David Portabella

3
สกาล่าสำหรับคนที่ต้องการเครื่องจักรเพื่อบอกพวกเขาว่าพวกเขาทำผิด
thedanotto

106

TL; DR

// For regular arrays:
var max = Math.max(...arrayOfNumbers);

// For arrays with tens of thousands of items:
let max = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
  if (testArray[i] > max) {
    max = testArray[i];
  }
}

โซลูชัน MDN

อย่างเป็นทางการเอกสาร MDN ในMath.max()แล้วครอบคลุมปัญหานี้:

ฟังก์ชันต่อไปนี้ใช้Function.prototype.apply ()เพื่อค้นหาองค์ประกอบสูงสุดในอาร์เรย์ตัวเลข getMaxOfArray([1, 2, 3])เทียบเท่ากับMath.max(1, 2, 3)แต่คุณสามารถใช้getMaxOfArray()กับอาร์เรย์ที่สร้างขึ้นแบบโปรแกรมได้ทุกขนาด

function getMaxOfArray(numArray) {
    return Math.max.apply(null, numArray);
}

หรือกับผู้ดำเนินการสเปรดใหม่การรับจำนวนอาเรย์สูงสุดจะกลายเป็นเรื่องง่ายขึ้นมาก

var arr = [1, 2, 3];
var max = Math.max(...arr);

ขนาดสูงสุดของอาร์เรย์

ตาม MDNapplyและการแพร่กระจายการแก้ปัญหาที่มีข้อ จำกัด ของ 65536 ที่มาจากการ จำกัด จำนวนสูงสุดของการขัดแย้งนี้:

แต่ระวัง: ในการใช้งานด้วยวิธีนี้คุณจะเสี่ยงเกินขีดจำกัดความยาวอาร์กิวเมนต์ของเครื่องมือ JavaScript ผลที่ตามมาของการใช้ฟังก์ชั่นที่มีข้อโต้แย้งมากเกินไป (คิดว่ามากกว่าหนึ่งหมื่นข้อโต้แย้ง) จะแตกต่างกันไปตามเอ็นจิ้น ( JavaScriptCore มีการ จำกัด อาร์กิวเมนต์แบบตายตัวจำนวน 65536 ) เนื่องจากขีด จำกัด (แน่นอนแม้กระทั่งธรรมชาติของกองซ้อนขนาดใหญ่ พฤติกรรม) ไม่ได้ระบุ เครื่องมือบางอย่างจะทำให้เกิดข้อยกเว้น ผู้อื่นจะ จำกัด จำนวนของข้อโต้แย้งที่ส่งผ่านไปยังฟังก์ชันที่ใช้อย่างไม่เป็นอันตราย เพื่อแสดงให้เห็นถึงกรณีหลังนี้: หากเครื่องยนต์มีขีด จำกัด สี่ข้อโต้แย้ง (ขีด จำกัด ที่แท้จริงมีค่าสูงกว่าอย่างมีนัยสำคัญ) มันจะเหมือนกับว่าข้อโต้แย้ง 5, 6, 2, 3 ถูกส่งผ่านไปใช้ในตัวอย่างด้านบน มากกว่าอาร์เรย์เต็ม

พวกเขายังให้บริการโซลูชั่นแบบไฮบริดซึ่งไม่ได้มีประสิทธิภาพที่ดีเมื่อเทียบกับโซลูชั่นอื่น ๆ ดูการทดสอบประสิทธิภาพด้านล่างเพิ่มเติม

ใน 2019 วงเงินที่เกิดขึ้นจริงเป็นขนาดสูงสุดของสาย stack สำหรับโครเมี่ยมตามเดสก์ทอปเบราว์เซอร์ที่ทันสมัยที่นี้หมายถึงว่าเมื่อมันมาถึงการหา min max / กับapplyหรือการแพร่กระจายจริงขนาดสูงสุดของตัวเลขเท่านั้นอาร์เรย์เป็น ~ เหนือสิ่งนี้จะมีสแต็คล้นและข้อผิดพลาดต่อไปนี้จะถูกโยน:

RangeError: เกินขนาดสแต็กการโทรสูงสุด

ด้วยสคริปต์ด้านล่าง (ตามการโพสต์บล็อกนี้ ) โดยการจับข้อผิดพลาดนั้นคุณสามารถคำนวณขีด จำกัด สำหรับสภาพแวดล้อมเฉพาะของคุณ

คำเตือน! การเรียกใช้สคริปต์นี้ต้องใช้เวลาและขึ้นอยู่กับประสิทธิภาพของระบบของคุณอาจทำให้เบราว์เซอร์หรือระบบของคุณช้าหรือขัดข้อง!

let testArray = Array.from({length: 10000}, () => Math.floor(Math.random() * 2000000));
for (i = 10000; i < 1000000; ++i) {
  testArray.push(Math.floor(Math.random() * 2000000));
  try {
    Math.max.apply(null, testArray);
  } catch (e) {
    console.log(i);
    break;
  }
}

ประสิทธิภาพในอาร์เรย์ขนาดใหญ่

จากการทดสอบในความคิดเห็นของEscapeNetscapeฉันได้สร้างเกณฑ์มาตรฐานบางอย่างที่ทดสอบ 5 วิธีที่แตกต่างกันในอาเรย์ตัวเลขสุ่มเท่านั้นที่มีรายการ 100,000 รายการรายการ

ในปี 2019 ผลลัพธ์แสดงให้เห็นว่าการวนซ้ำมาตรฐาน (ซึ่ง BTW ไม่มีขนาด จำกัด ) นั้นเร็วที่สุดในทุกที่ applyและแพร่กระจายอย่างใกล้ชิดหลังจากนั้นจากนั้นโซลูชันไฮบริดของ MDN ในเวลาต่อมาreduceช้าที่สุด

การทดสอบเกือบทั้งหมดให้ผลลัพธ์เหมือนกันยกเว้นการทดสอบที่ส่วนใหญ่กระจายไปทั่วทำให้ช้าที่สุด

หากคุณเพิ่มอาร์เรย์ให้มี 1 ล้านรายการสิ่งต่าง ๆ จะเริ่มแตกและคุณจะเหลือวงมาตรฐานเป็นวิธีแก้ปัญหาที่รวดเร็วและreduceช้ากว่า

มาตรฐาน JSPerf

jsperf.com เป็นเกณฑ์มาตรฐานสำหรับการแก้ปัญหาต่าง ๆ เพื่อค้นหารายการต่ำสุด / สูงสุดของอาร์เรย์

มาตรฐาน JSBen

ผลการเปรียบเทียบ jsben.com สำหรับวิธีแก้ไขปัญหาต่าง ๆ เพื่อค้นหารายการต่ำสุด / สูงสุดของอาร์เรย์

มาตรฐาน JSBench.me

ผลลัพธ์การเปรียบเทียบ jsbench.me สำหรับโซลูชันที่แตกต่างเพื่อค้นหารายการต่ำสุด / สูงสุดของอาร์เรย์

รหัสที่มามาตรฐาน


หากคุณใช้ typescript ตัวดำเนินการสเปรดดังที่แสดงไว้จะถูกรวบรวมเพื่อMath.max.apply(Math, arr)ความเข้ากันได้ 'สูงสุด'
Simon_Weaver

1
นอกจากนี้จาก MDN: "ทั้งการแพร่กระจาย(...)และapplyอาจล้มเหลวหรือส่งคืนผลลัพธ์ที่ผิดถ้าอาร์เรย์มีองค์ประกอบมากเกินไป [... ] การแก้ปัญหาลดไม่ได้มีปัญหานี้" การทดสอบ Chrome, FF, Edge และ IE11 ดูเหมือนว่ามันเป็น ตกลงสำหรับอาร์เรย์ที่มีค่ามากถึง 100k (ทดสอบบน Win10 และเบราว์เซอร์ล่าสุด: Chrome 110k, Firefox 300k, Edge 400k, IE11 150k)
oriadam

นี่เป็นวิธีที่ช้ามากจะเกิดอะไรขึ้นถ้าอาร์เรย์มีองค์ประกอบหลายพันรายการ
Slava Fomin II

@SlavaFominII ฉันขยายคำตอบเพื่อให้ครอบคลุมอาร์เรย์ที่มีองค์ประกอบมากมาย
totymedli

68

หากคุณหวาดระแวงเหมือนฉันเกี่ยวกับการใช้Math.max.apply(ซึ่งอาจทำให้เกิดข้อผิดพลาดเมื่อได้รับอาร์เรย์ขนาดใหญ่ตาม MDN ) ลองสิ่งนี้:

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.max(a, b);
  });
}

function arrayMin(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  });
}

หรือใน ES6:

function arrayMax(array) {
  return array.reduce((a, b) => Math.max(a, b));
}

function arrayMin(array) {
  return array.reduce((a, b) => Math.min(a, b));
}

ฟังก์ชั่นที่ไม่ระบุชื่อที่น่าเสียดายที่มีความจำเป็น (แทนการใช้Math.max.bind(Math)เพราะreduceไม่เพียงแค่ผ่านaและbฟังก์ชั่นของมัน แต่ยังiและมีการอ้างอิงไปยังอาร์เรย์ของตัวเองดังนั้นเราจึงต้องให้แน่ใจว่าเราไม่พยายามที่จะเรียกmaxในเหล่านั้นเช่นกัน


ตัวอย่าง ES6 ของคุณมีเหตุผลใดที่ทำให้ไม่เพียงแค่กลับมาMath.max(...array)?
Wojciech Bednarski

@WojciechBednarski หน้านี้ดูเหมือนจะแนะนำว่าการใช้ตัวดำเนินการสเปรดนั้นเหมือนกับการส่งผ่านอาร์เรย์ไปยังapplyดังนั้นจึงมีข้อเสียเดียวกัน (ขีด จำกัด อาร์กิวเมนต์สูงสุด)
Daniel Buckmaster

ขอบคุณสำหรับสิ่งนี้. เพียงคุณสามารถแก้ไขวงเล็บที่ขาดหายไปหลังจากลด:function arrayMax(array) { return array.reduce(function(a, b) { return Math.max(a, b); }); // <--------- missing ) }
Arkowsky

1
@DanielDietrich ฉันเดาว่าทำสิ่งที่เทียบเท่าได้Math.min()โดยไม่ต้องเรียกค่าใด ๆ ส่งคืนInfinityดังนั้นฟังก์ชันเหล่านี้สามารถใช้reduce(..., Infinity)เพื่อจับคู่พฤติกรรมนั้นได้ ฉันชอบที่จะโยนข้อยกเว้น แต่ (เหมือนที่เป็นอยู่ในปัจจุบัน) เนื่องจากการใช้อาร์เรย์ขั้นต่ำที่ว่างเปล่าน่าจะเป็นข้อผิดพลาด
Daniel Buckmaster

1
จนถึงตอนนี้ก็คือช้าที่สุด
АлексейЛещук

40

.apply มักจะถูกใช้เมื่อความตั้งใจที่จะเรียกใช้ฟังก์ชัน Variadic พร้อมกับรายการค่าอาร์กิวเมนต์เช่น

Math.max([value1[,value2, ...]])ฟังก์ชันส่งกลับที่ใหญ่ที่สุดของศูนย์หรือมากกว่าตัวเลข

Math.max(10, 20); // 20
Math.max(-10, -20); // -10
Math.max(-10, 20); // 20

Math.max()วิธีการไม่อนุญาตให้คุณที่จะผ่านในอาร์เรย์ หากคุณมีรายการค่าที่คุณต้องการรับมากที่สุดโดยปกติคุณจะเรียกใช้ฟังก์ชันนี้โดยใช้Function.prototype.apply ()เช่น

Math.max.apply(null, [10, 20]); // 20
Math.max.apply(null, [-10, -20]); // -10
Math.max.apply(null, [-10, 20]); // 20

อย่างไรก็ตามในECMAScript 6คุณสามารถใช้โอเปอเรเตอร์การแพร่กระจาย :

ตัวดำเนินการการแพร่กระจายช่วยให้การแสดงออกที่จะขยายในสถานที่ที่มีข้อโต้แย้งหลาย ๆ (สำหรับการเรียกใช้ฟังก์ชั่น) หรือหลายองค์ประกอบ (สำหรับตัวอักษรอาร์เรย์) คาดว่า

การใช้ตัวดำเนินการสเปรดด้านบนสามารถเขียนใหม่ได้เช่น:

Math.max(...[10, 20]); // 20
Math.max(...[-10, -20]); // -10
Math.max(...[-10, 20]); // 20

เมื่อเรียกใช้ฟังก์ชันโดยใช้โอเปอเรเตอร์ variadic คุณสามารถเพิ่มค่าเพิ่มเติมได้เช่น

Math.max(...[10, 20], 50); // 50
Math.max(...[-10, -20], 50); // 50

โบนัส:

ผู้ประกอบการแพร่กระจายช่วยให้คุณใช้อาร์เรย์ไวยากรณ์ที่แท้จริงในการสร้างอาร์เรย์ใหม่ในสถานการณ์ที่ใน ES5 คุณจะต้องถอยกลับไปยังรหัสความจำเป็นโดยใช้การรวมกันของpush, spliceฯลฯ

let foo = ['b', 'c'];
let bar = ['a', ...foo, 'd', 'e']; // ['a', 'b', 'c', 'd', 'e']

1
ตัวอย่างสุดท้ายของคุณในโบนัสจะถูกเขียนขึ้นconcatโดยโปรแกรมเมอร์ส่วนใหญ่เพราะมันช่วยให้คุณรักษาสไตล์บรรทัดเดียว
Cody Allan Taylor

31

สองวิธีที่สั้นกว่าและง่าย:

let arr = [2, 6, 1, 0]

วิธีที่ 1 :

let max = Math.max.apply(null, arr)

วิธีที่ 2 :

let max = arr.reduce(function(a, b) {
    return Math.max(a, b);
});

ระวังว่าอาร์เรย์นั้นว่างเปล่าหรือไม่คุณจะได้รับอินฟินิตี้ลบซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ หากคุณต้องการที่จะได้รับ0คุณสามารถใช้[0].concat(arr)หรือมีการแพร่กระจายไวยากรณ์[0, ...arr](ในสถานที่ของ 'ARR)
Simon_Weaver

มีวิธีการยกเว้นค่าว่างในวิธีที่ 1 หรือไม่
bonbon.langes

22

คุณทำได้โดยขยายประเภท Array:

Array.max = function( array ){
    return Math.max.apply( Math, array );
};
Array.min = function( array ){
    return Math.min.apply( Math, array );
}; 

เพิ่มขึ้นจากที่นี่ (โดย John Resig)


20

วิธีง่ายๆในการหาค่าต่ำสุดArrayขององค์ประกอบคือการใช้Arrayฟังก์ชั่นต้นแบบreduce:

A = [4,3,-9,-2,2,1];
A.reduce((min, val) => val < min ? val : min, A[0]); // returns -9

หรือใช้ฟังก์ชัน Math.Min () ในตัวของ JavaScript (ขอบคุณ @Tenflex):

A.reduce((min,val) => Math.min(min,val), A[0]);

ชุดนี้minไปA[0]และการตรวจสอบแล้วไม่ว่าจะเป็นอย่างเคร่งครัดน้อยกว่าปัจจุบันA[1]...A[n] minหากA[i] < minแล้วมีการปรับปรุงเพื่อmin A[i]เมื่อองค์ประกอบอาเรย์ทั้งหมดได้รับการประมวลผลminจะถูกส่งคืนเป็นผลลัพธ์

แก้ไข : รวมตำแหน่งของค่าต่ำสุด:

A = [4,3,-9,-2,2,1];
A.reduce((min, val) => val < min._min ? {_min: val, _idx: min._curr, _curr: min._curr + 1} : {_min: min._min, _idx: min._idx, _curr: min._curr + 1}, {_min: A[0], _idx: 0, _curr: 0}); // returns { _min: -9, _idx: 2, _curr: 6 }

3
A.reduce ((นาที, val) => Math.min (นาที, val), A [0]); ยิ่งสั้น
Tenflex

ในฐานะที่เป็นคำถามโบนัสวิธีการได้รับminมูลค่าไม่เพียงแต่ส่งกลับ แต่ยังอยู่ในตำแหน่งของมันในอาร์เรย์?
stevek

@meshfields - ฉันได้อัพเดตคำตอบแล้ว
Nicolas Lykke Iversen

15

คนอื่น ๆ ได้ให้คำตอบบางอย่างกับพวกเขาArray.prototypeแล้ว ทั้งหมดที่ฉันต้องการในคำตอบนี้คือการชี้แจงว่ามันควรจะเป็นหรือMath.min.apply( Math, array ) ดังนั้นสิ่งที่บริบทควรใช้หรือ?Math.min.apply( null, array )Mathnull

เมื่อผ่านnullเป็นบริบทไปapplyแล้วบริบทจะเริ่มต้นที่วัตถุทั่วโลก ( windowวัตถุในกรณีของเบราว์เซอร์) ผ่านMathวัตถุที่เป็นบริบทจะเป็นทางออกที่ถูกต้อง แต่มันจะไม่ทำร้ายผ่านnullอย่างใดอย่างหนึ่ง นี่คือตัวอย่างเมื่อnullอาจทำให้เกิดปัญหาเมื่อตกแต่งMath.maxฟังก์ชั่น:

// decorate Math.max
(function (oldMax) {
    Math.max = function () {
        this.foo(); // call Math.foo, or at least that's what we want

        return oldMax.apply(this, arguments);
    };
})(Math.max);

Math.foo = function () {
    print("foo");
};

Array.prototype.max = function() {
  return Math.max.apply(null, this); // <-- passing null as the context
};

var max = [1, 2, 3].max();

print(max);

ข้างต้นจะโยนข้อยกเว้นเพราะthis.fooจะได้รับการประเมินว่าเป็นซึ่งเป็นwindow.foo undefinedหากเราแทนที่nullด้วยMathสิ่งต่าง ๆ จะทำงานได้ตามที่คาดหวังและสตริง "foo" จะถูกพิมพ์ไปที่หน้าจอ (ฉันทดสอบโดยใช้Mozilla Rhino )

คุณสามารถสมมติได้ว่าไม่มีใครตกแต่งMath.maxดังนั้นการส่งผ่านnullจะทำได้โดยไม่มีปัญหา


2
จุดที่ถ่าย อย่างไรก็ตามทำไมมีคนตกแต่งFoo.staticMethodและอ้างอิงthis? นั่นจะไม่เป็นความผิดพลาดในการออกแบบมัณฑนากรหรือไม่? (เว้นแต่แน่นอนว่าพวกเขาต้องการอ้างอิงขอบเขตทั่วโลกและต้องการรักษาความเป็นอิสระของเครื่องมือ JavaScript ที่ใช้เช่น Rhino)
Roatin Marth

1
ข้อมูลจำเพาะมีความชัดเจนเกี่ยวกับฟังก์ชันที่ระบุควรอ้างอิงถึง " ค่านี้ " (จริง ๆ แล้ววลีนั้นจะปรากฏขึ้น 125 ครั้งในข้อมูลจำเพาะ) ดำเนินการต่อสเปคไม่ได้ใช้Math.max thisหากใครบางคนลบล้างMath.maxสิ่งที่มันใช้thisแล้วพวกเขาได้ทำพฤติกรรมของมันละเมิดข้อมูลจำเพาะและคุณควรโยนวัตถุมีคมที่พวกเขา คุณไม่ควรเขียนรหัสเกี่ยวกับความเป็นไปได้นั้นมากกว่าที่คุณจะเขียนรหัสความเป็นไปได้ที่บางคนเปลี่ยนไปMath.maxและMath.minสำหรับ lulz
Mark Amery

15

อีกวิธีหนึ่งในการทำ:

var arrayMax = Function.prototype.apply.bind(Math.max, null);

การใช้งาน:

var max = arrayMax([2, 5, 1]);

บางคนสามารถอธิบายได้ว่ามันใช้งานได้อย่างไร? นี่คือยาเสพติดสวย ความเข้าใจของฉันถูกต้องหรือไม่: arrayMax เป็นฟังก์ชั่นและเราผูกบางอย่างเข้ากับคุณสมบัติของต้นแบบ สิ่งนี้ใช้บังคับผูกและต้นแบบทุกตัวมีหรือไม่
Sam

คุณสามารถดูได้ที่: benalman.com/news/2012/09/partial-application-in-javascript
sbr

14

วิธีการทางเลือก


Math.minและMath.maxวิธีการดำเนินงานมีทั้ง recursive ที่ถูกเพิ่มเข้าไปในเครื่องยนต์ของ JS โทรกองและส่วนใหญ่ความผิดพลาดสำหรับอาร์เรย์ที่มีจำนวนมากของรายการ
(มากกว่า ~ 10⁷รายการขึ้นอยู่กับเบราว์เซอร์ของผู้ใช้)

Math.max ( ... อาร์เรย์ (1000000) .keys ());

Uncaught RangeError: เกินขนาดสแต็กการโทรสูงสุด

ให้ใช้สิ่งที่ต้องการแทน:

arr.reduce((max, val) => max > val ? max : val, arr[0])

หรือดีกว่าเวลาทำงาน:

function maxValue(arr) {
  let max = arr[0];

  for (let val of arr) {
    if (val > max) {
      max = val;
    }
  }
  return max;
}

หรือรับทั้ง Min และ Max:

function getMinMax(arr) {
  return arr.reduce(({min, max}, v) => ({
    min: min < v ? min : v,
    max: max > v ? max : v,
  }), { min: arr[0], max: arr[0] });
}

หรือด้วยเวลาทำงานที่ดียิ่งขึ้น *:

function getMinMax(arr) {
  let min = arr[0];
  let max = arr[0];
  let i = arr.length;

  while (i--) {
    min = arr[i] < min ? arr[i] : min;
    max = arr[i] > max ? arr[i] : max;
  }
  return { min, max };
}

* ทดสอบ 1,000,000 รายการ:
สำหรับการอ้างอิงฟังก์ชั่นที่ 1 รันไทม์ (บนเครื่องของฉัน) คือ 15.84ms เทียบกับฟังก์ชั่นที่ 2 มีเพียง 4.32ms


13

สิ่งนี้อาจเหมาะสมกับวัตถุประสงค์ของคุณ

Array.prototype.min = function(comparer) {

    if (this.length === 0) return null;
    if (this.length === 1) return this[0];

    comparer = (comparer || Math.min);

    var v = this[0];
    for (var i = 1; i < this.length; i++) {
        v = comparer(this[i], v);    
    }

    return v;
}

Array.prototype.max = function(comparer) {

    if (this.length === 0) return null;
    if (this.length === 1) return this[0];

    comparer = (comparer || Math.max);

    var v = this[0];
    for (var i = 1; i < this.length; i++) {
        v = comparer(this[i], v);    
    }

    return v;
}

คุณควรเริ่มต้น v ของคุณด้วย 'this [0]' ในกรณีที่ไม่มีตัวเลขใด ๆ ที่น้อยกว่า 0
jasonmw

คือcomparerควรจะเรียกว่าอยู่ในขอบเขตที่เฉพาะเจาะจงบางอย่าง? เพราะมันคือการอ้างอิงthis[index]ซึ่งเป็นundefinedทุกครั้ง
Roatin Marth

คงที่ฉันมักจะลืมเกี่ยวกับการกำหนดขอบเขตระดับฟังก์ชั่น
ChaosPandion

โอ้ตอนนี้ตอนนี้ @Ionut จีสแตนจะวิจารณ์สำหรับอาร์กิวเมนต์เดียวกัน "ผิดบริบท" ในขณะที่เขาทำผมตั้งแต่ Comparer เริ่มต้นของคุณ ( Math.xxx) จะทำงานในขอบเขตทั่วโลก ...
Roatin Marth

นั่นอาจเป็นจริง แต่ลายเซ็นของฟังก์ชั่นใหม่นั้นไม่จำเป็นต้องมีขอบเขตเนื่องจากมันจะนำวัตถุ 2 ชิ้นที่ต้องนำมาเปรียบเทียบ
ChaosPandion


12

ฉันประหลาดใจที่ไม่มีใครพูดถึงฟังก์ชั่นลด

var arr = [1, 10, 5, 11, 2]

var b = arr.reduce(function(previous,current){ 
                      return previous > current ? previous:current
                   });

b => 11
arr => [1, 10, 5, 11, 2]

ระวัง: ลด () ได้รับการสนับสนุนจาก IE9 ดูที่developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Paul Gobée

1
ฉันไม่สามารถใช้สิ่งนี้ใน Chromium รุ่นปัจจุบันได้
PJSCopeland

9

สำหรับอาร์เรย์ขนาดใหญ่ (องค์ประกอบ ~ 10⁷) Math.minและMath.maxจัดหา RangeError (เกินขนาดสแต็กการโทรสูงสุด) ใน node.js

สำหรับอาร์เรย์ขนาดใหญ่โซลูชันที่รวดเร็วและสกปรกคือ:

Array.prototype.min = function() {
    var r = this[0];
    this.forEach(function(v,i,a){if (v<r) r=v;});
    return r;
};

8

ฉันมีปัญหาเดียวกันฉันจำเป็นต้องได้รับค่าต่ำสุดและสูงสุดของอาร์เรย์และเพื่อความประหลาดใจของฉันไม่มีฟังก์ชั่นในตัวสำหรับอาร์เรย์ หลังจากอ่านมากฉันตัดสินใจทดสอบโซลูชัน "top 3" ด้วยตัวเอง:

  1. การแก้ปัญหาแบบไม่ต่อเนื่อง: การวนรอบ FOR เพื่อตรวจสอบทุกองค์ประกอบของอาร์เรย์กับค่าสูงสุดและ / หรือต่ำสุดในปัจจุบัน
  2. ใช้วิธีแก้ปัญหา: ส่งอาร์เรย์ไปยังฟังก์ชันภายใน Math.max และ / หรือ Math.min โดยใช้ Apply (null, array)
  3. โซลูชัน REDUCE: เรียกใช้การตรวจสอบซ้ำกับทุกองค์ประกอบของอาร์เรย์โดยใช้การลด (ฟังก์ชัน)

รหัสการทดสอบคือ:

function GetMaxDISCRETE(A)
{   var MaxX=A[0];

    for (var X=0;X<A.length;X++)
        if (MaxX<A[X])
            MaxX=A[X];

    return MaxX;
}

function GetMaxAPPLY(A)
{   return Math.max.apply(null,A);
}

function GetMaxREDUCE(A)
{   return A.reduce(function(p,c)
    {   return p>c?p:c;
    });
}

อาเรย์ A นั้นเต็มไปด้วยตัวเลขสุ่มจำนวนเต็ม 100,000 ตัวโดยแต่ละฟังก์ชั่นจะถูกประมวลผล 10,000 ครั้งบน Mozilla Firefox 28.0 บนเดสก์ท็อป Intel Pentium 4 2.99GHz พร้อม Windows Vista เวลาอยู่ในหน่วยวินาทีที่ดึงข้อมูลโดย function.now () ฟังก์ชัน ผลการวิจัยพบว่ามีตัวเลข 3 หลักและส่วนเบี่ยงเบนมาตรฐาน:

  1. โซลูชันที่ไม่ต่อเนื่อง: mean = 0.161s, sd = 0.078
  2. ใช้วิธีแก้ปัญหา: mean = 3.571s, sd = 0.487
  3. วิธีแก้ปัญหาลดลง: mean = 0.350s, sd = 0.044

โซลูชัน REDUCE ช้ากว่าโซลูชันที่ไม่ต่อเนื่อง 117% ทางออกที่แย่กว่านั้นคือ 2,118% ช้ากว่าโซลูชั่นที่ไม่ต่อเนื่อง นอกจากนี้ตามที่ Peter ตั้งข้อสังเกตมันไม่ได้ผลสำหรับอาร์เรย์ขนาดใหญ่ (ประมาณ 1,000,000 องค์ประกอบ)

นอกจากนี้เพื่อทำการทดสอบฉันได้ทดสอบโค้ดที่ไม่ต่อเนื่องนี้แบบขยาย:

var MaxX=A[0],MinX=A[0];

for (var X=0;X<A.length;X++)
{   if (MaxX<A[X])
        MaxX=A[X];
    if (MinX>A[X])
        MinX=A[X];
}

ระยะเวลา: mean = 0.218s, sd = 0.094

ดังนั้นจึงช้ากว่าโซลูชันที่ไม่ต่อเนื่อง 35% แต่จะดึงทั้งค่าสูงสุดและค่าต่ำสุดในครั้งเดียว (โซลูชันอื่นใดจะใช้เวลาอย่างน้อยสองครั้งเพื่อเรียกคืน) เมื่อ OP ต้องการทั้งสองค่าโซลูชันที่ไม่ต่อเนื่องจะเป็นตัวเลือกที่ดีที่สุด (แม้จะมีฟังก์ชั่นที่แยกกันสองฟังก์ชั่นหนึ่งสำหรับการคำนวณสูงสุดและอีกอันสำหรับการคำนวณขั้นต่ำพวกเขาจะทำได้ดีกว่า


8

คุณสามารถใช้ฟังก์ชั่นต่อไปนี้ได้ทุกที่ในโครงการของคุณ:

function getMin(array){
    return Math.min.apply(Math,array);
}

function getMax(array){
    return Math.max.apply(Math,array);
}

จากนั้นคุณสามารถเรียกใช้ฟังก์ชันผ่านอาร์เรย์ได้

var myArray = [1,2,3,4,5,6,7];
var maximo = getMax(myArray); //return the highest number

8

รหัสต่อไปนี้ใช้งานได้สำหรับฉัน:

var valueList = [10,4,17,9,3];
var maxValue = valueList.reduce(function(a, b) { return Math.max(a, b); });
var minValue = valueList.reduce(function(a, b) { return Math.min(a, b); });

7

วนซ้ำตามรอยตามที่คุณไป

var min = null;
var max = null;
for (var i = 0, len = arr.length; i < len; ++i)
{
    var elem = arr[i];
    if (min === null || min > elem) min = elem;
    if (max === null || max < elem) max = elem;
}
alert( "min = " + min + ", max = " + max );

สิ่งนี้จะปล่อยให้ min / max null เป็นโมฆะหากไม่มีองค์ประกอบในอาร์เรย์ จะตั้งค่า min และ max ในหนึ่งรอบถ้าอาร์เรย์มีองค์ประกอบใด ๆ

คุณสามารถขยาย Array ด้วยrangeวิธีการที่ใช้ด้านบนเพื่อให้สามารถนำมาใช้ซ้ำและปรับปรุงการอ่านได้ ดูซอที่ใช้งานได้ที่http://jsfiddle.net/9C9fU/

Array.prototype.range = function() {

    var min = null,
        max = null,
        i, len;

    for (i = 0, len = this.length; i < len; ++i)
    {
        var elem = this[i];
        if (min === null || min > elem) min = elem;
        if (max === null || max < elem) max = elem;
    }

    return { min: min, max: max }
};

ใช้เป็น

var arr = [3, 9, 22, -7, 44, 18, 7, 9, 15];

var range = arr.range();

console.log(range.min);
console.log(range.max);

@JordanDillonChapian ฉันเห็นด้วย แต่มันเป็นเรื่องง่ายที่จะขยายไปยังrangeฟังก์ชั่นที่จะเป็นวิธีที่ดีที่สุดในการรับทั้งนาทีและสูงสุดในเวลาเดียวกัน IMO - เช่นเดียวกับที่ฉันทำกับการอัพเดตคำตอบของฉัน
tvanfosson

6

ฉันคิดว่าฉันจะแบ่งปันวิธีแก้ปัญหาที่ง่ายและเข้าใจง่าย

สำหรับนาที:

var arr = [3, 4, 12, 1, 0, 5];
var min = arr[0];
for (var k = 1; k < arr.length; k++) {
  if (arr[k] < min) {
    min = arr[k];
  }
}
console.log("Min is: " + min);

และสำหรับสูงสุด:

var arr = [3, 4, 12, 1, 0, 5];
var max = arr[0];
for (var k = 1; k < arr.length; k++) {
  if (arr[k] > max) {
    max = arr[k];
  }
}
console.log("Max is: " + max);



ขอบคุณ ฉันเปลี่ยนคำตอบ
Ionut Necula

การวนซ้ำยังคงไม่ถูกต้อง (การเข้าถึงคุณสมบัติที่ไม่มีอยู่)
Bergi

มีอะไรผิดปกติฉันไม่เห็นอะไรผิดปกติ คุณช่วยเสนอตัวอย่างได้ไหม
Ionut Necula

1
แก้ไขตามทันที หวังว่าฉันเข้าใจคุณอย่างถูกต้อง
Ionut Necula

6

นอกเหนือจากการใช้ฟังก์ชั่นคณิตศาสตร์ max และ min แล้วฟังก์ชั่นอื่นที่จะใช้ก็คือฟังก์ชั่นการเรียง (): ในตัวเราไปกันเลย

const nums = [12, 67, 58, 30].sort((x, y) => 
x -  y)
let max = nums[0]
let min = nums[nums.length -1]

5

เรื่องง่าย ๆ จริงๆ

var arr = [10,20,30,40];
arr.max = function() { return  Math.max.apply(Math, this); }; //attach max funct
arr.min = function() { return  Math.min.apply(Math, this); }; //attach min funct

alert("min: " + arr.min() + " max: " + arr.max());

5

นี่เป็นวิธีหนึ่งในการรับค่าสูงสุดจากอาร์เรย์ของวัตถุ สร้างสำเนา (พร้อมชิ้น) จากนั้นเรียงลำดับสำเนาตามลำดับจากมากไปน้อยแล้วหยิบรายการแรก

var myArray = [
    {"ID": 1, "Cost": 200},
    {"ID": 2, "Cost": 1000},
    {"ID": 3, "Cost": 50},
    {"ID": 4, "Cost": 500}
]

maxsort = myArray.slice(0).sort(function(a, b) { return b.ID - a.ID })[0].ID; 

5

ใช้Math.max()หรือMath.min()

Math.max(10, 20);   //  20
Math.min(-10, -20); // -20

ฟังก์ชั่นต่อไปนี้ใช้Function.prototype.apply()ในการค้นหาองค์ประกอบสูงสุดในอาร์เรย์ที่เป็นตัวเลข getMaxOfArray([1, 2, 3])เทียบเท่ากับMath.max(1, 2, 3)แต่คุณสามารถใช้getMaxOfArray()กับอาร์เรย์ที่สร้างขึ้นแบบโปรแกรมได้ทุกขนาด

function getMaxOfArray(numArray) {
  return Math.max.apply(null, numArray);
}

หรือกับผู้ดำเนินการสเปรดใหม่การรับจำนวนอาเรย์สูงสุดจะกลายเป็นเรื่องง่ายขึ้นมาก

var arr = [1, 2, 3];
var max = Math.max(...arr); // 3
var min = Math.min(...arr); // 1

4

วิธีการแก้ปัญหาของ ChaosPandion ใช้งานได้หากคุณใช้โพรโทไพ ถ้าไม่พิจารณาสิ่งนี้:

Array.max = function( array ){
    return Math.max.apply( Math, array );
};

Array.min = function( array ){
    return Math.min.apply( Math, array );
};

ด้านบนจะส่งคืน NaN หากค่าอาร์เรย์ไม่ใช่จำนวนเต็มดังนั้นคุณควรสร้างฟังก์ชันการทำงานบางอย่างเพื่อหลีกเลี่ยงปัญหานั้น มิฉะนั้นจะใช้งานได้


ทำไมคุณมี (คณิตศาสตร์สิ่งนี้) ในฐานะ agruments เมื่อ Roatin Marth มีเพียง (null สิ่งนี้)
HankH

@HankH: ดูการตอบสนองต่อความคิดเห็นของคุณในความคิดเห็นต่อคำตอบของฉันเอง
Roatin Marth

1
ฉันไม่เข้าใจสิ่งที่คุณหมายถึงโดย "วิธีการแก้ปัญหาของ ChaosPandion ทำงานถ้าคุณใช้ protoype" วิธีการแก้ปัญหาของคุณแตกต่างกันอย่างไรยกเว้นว่าคุณใช้Mathวัตถุเป็นบริบท
Ionuț G. Stan

ขออภัยฉันหมายถึงถ้าคุณขยายต้นแบบของคุณจะทำงาน ขอโทษ.
jay

ดังนั้นไหนดีกว่า jeerose หรือ ChaosPandion
HankH

3

หากคุณใช้ไลบรารีsugar.jsคุณสามารถเขียนarr.min ()และarr.max ()ตามที่คุณแนะนำ นอกจากนี้คุณยังสามารถรับค่า min และ max ได้จากอาร์เรย์ที่ไม่ใช่ตัวเลข

min (map, all = false)ส่งคืนองค์ประกอบในอาร์เรย์ด้วยค่าต่ำสุด แผนที่อาจเป็นฟังก์ชั่นการทำแผนที่ค่าที่จะตรวจสอบหรือสตริงที่ทำหน้าที่เป็นทางลัด หากทั้งหมดเป็นจริงจะส่งคืนค่าขั้นต่ำทั้งหมดในอาร์เรย์

max (map, all = false)ส่งคืนองค์ประกอบในอาร์เรย์ด้วยค่าที่มากที่สุด แผนที่อาจเป็นฟังก์ชั่นการทำแผนที่ค่าที่จะตรวจสอบหรือสตริงที่ทำหน้าที่เป็นทางลัด หากเป็นจริงจะส่งคืนค่าสูงสุดทั้งหมดในอาร์เรย์

ตัวอย่าง:

[1,2,3].min() == 1
['fee','fo','fum'].min('length') == "fo"
['fee','fo','fum'].min('length', true) == ["fo"]
['fee','fo','fum'].min(function(n) { return n.length; }); == "fo"
[{a:3,a:2}].min(function(n) { return n['a']; }) == {"a":2}
['fee','fo','fum'].max('length', true) == ["fee","fum"]

ไลบรารี่เช่นLo-Dashและunderscore.jsยังมีฟังก์ชั่น min และ max อันทรงพลังเช่นเดียวกัน:

ตัวอย่างจาก Lo-Dash:

_.max([4, 2, 8, 6]) == 8
var characters = [
  { 'name': 'barney', 'age': 36 },
  { 'name': 'fred',   'age': 40 }
];
_.max(characters, function(chr) { return chr.age; }) == { 'name': 'fred', 'age': 40 }

3
let arr = [2,5,3,5,6,7,1];

let max = Math.max(...arr); // 7
let min = Math.min(...arr); // 1

2
โซลูชันนี้มีให้โดยผู้ตอบคำถามอื่นหลายคนแล้ว คำตอบของคุณเพิ่มอะไร?
นิค

3

ลอง

let max= a=> a.reduce((m,x)=> m>x ? m:x);
let min= a=> a.reduce((m,x)=> m<x ? m:x);

สำหรับ Math.min / max (+ Apply) เราได้รับข้อผิดพลาด:

เกินจำนวนสแต็กการโทรสูงสุด (Chrome 74.0.3729.131)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.