على - شرح javascript




دمج/تسطيح مجموعة من المصفوفات في جافا سكريبت؟ (20)

أفضل تحويل الصفيف بأكمله ، كما هو ، إلى سلسلة ، ولكن بخلاف الإجابات الأخرى ، سيفعل ذلك باستخدام JSON.stringify ولا يستخدم طريقة toString() ، التي تنتج نتيجة غير مرغوب فيها.

باستخدام هذا المخرج JSON.stringify ، كل ما تبقى هو إزالة جميع الأقواس ، مع التفاف النتيجة مع قوسين البداية والنهاية مرة أخرى ، وتقديم النتيجة مع JSON.parse الذي يعيد السلسلة إلى "الحياة".

  • يمكن التعامل مع صفائف متداخلة لانهائية دون أي تكاليف السرعة.
  • يمكن التعامل مع عناصر المصفوفة التي هي سلاسل تحتوي على فواصل.

var arr = ["abc",[[[6]]],["3,4"],"2"];

var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]";
var flattened = JSON.parse(s);

console.log(flattened)

  • فقط لمجموعة صفيف متعددة الأعداد / الأرقام (وليس الأجسام)

لدي مصفوفة جافا سكريبت مثل:

[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]

كيف أذهب عن جعل هذا فقط:

["$6", "$12", "$25", ...]

إذا كان لديك صفائف مع عنصر سلسلة واحد فقط:

[["$6"], ["$12"], ["$25"], ["$25"]].join(',').split(',');

سوف تقوم بهذه المهمة. Bt الذي يطابق على وجه التحديد مثال الكود الخاص بك.


تعني الإجراءات العامة أنه لا يتعين علينا إعادة كتابة التعقيد في كل مرة نحتاج فيها إلى استخدام سلوك معين.

concatMap (أو flatMap ) هو بالضبط ما نحتاجه في هذه الحالة.

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.map(f).reduce(concat, [])

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)

// your sample data
const data =
  [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]

console.log (flatten (data))

بصيرة

ونعم ، كنت تفكر في ذلك بشكل صحيح ، إلا أنه يسوي مستوى واحد ، وهو بالضبط كيف يجب أن تعمل

تخيل بعض مجموعة البيانات مثل هذا

// Player :: (String, Number) -> Player
const Player = (name,number) =>
  [ name, number ]

// team :: ( . Player) -> Team
const Team = (...players) =>
  players

// Game :: (Team, Team) -> Game
const Game = (teamA, teamB) =>
  [ teamA, teamB ]

// sample data
const teamA =
  Team (Player ('bob', 5), Player ('alice', 6))

const teamB =
  Team (Player ('ricky', 4), Player ('julian', 2))

const game =
  Game (teamA, teamB)

console.log (game)
// [ [ [ 'bob', 5 ], [ 'alice', 6 ] ],
//   [ [ 'ricky', 4 ], [ 'julian', 2 ] ] ]

حسنًا ، لنفترض الآن أننا نريد طباعة قائمة تعرض جميع اللاعبين الذين سيشاركون في game ...

const gamePlayers = game =>
  flatten (game)

gamePlayers (game)
// => [ [ 'bob', 5 ], [ 'alice', 6 ], [ 'ricky', 4 ], [ 'julian', 2 ] ]

إذا كان flatten المسطح قد سطح صفائف متداخلة أيضًا ، فسينتهي بنا الأمر مع هذه النتيجة.

const gamePlayers = game =>
  badGenericFlatten(game)

gamePlayers (game)
// => [ 'bob', 5, 'alice', 6, 'ricky', 4, 'julian', 2 ]

رولين عميق ، حبيبتي

هذا لا يعني في بعض الأحيان أنك لا تريد تسوية المصفوفات المتداخلة أيضًا - فقط هذا لا ينبغي أن يكون السلوك الافتراضي.

يمكننا إجراء إجراء deepFlatten بكل سهولة ...

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.map(f).reduce(concat, [])

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)

// deepFlatten :: [[a]] -> [a]
const deepFlatten =
  concatMap (x =>
    Array.isArray (x) ? deepFlatten (x) : x)

// your sample data
const data =
  [0, [1, [2, [3, [4, 5], 6]]], [7, [8]], 9]

console.log (flatten (data))
// [ 0, 1, [ 2, [ 3, [ 4, 5 ], 6 ] ], 7, [ 8 ], 9 ]

console.log (deepFlatten (data))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

هناك. الآن لديك أداة لكل وظيفة - واحدة لسحق مستوى واحد من التعشيش ، flatten ، وواحد لطمس كل deepFlatten .

ربما يمكنك deepFlatten أو obliterate إذا كنت لا تحب الاسم deepFlatten .

لا تتكرر مرتين!

وبالطبع فإن التطبيقات المذكورة أعلاه ذكية ومختصرة ، ولكن باستخدام .map متبوعة بدعوة إلى .reduce يعني أننا نقوم بالفعل .reduce أكثر من اللازم

باستخدام mapReduce أنا اتصل mapReduce يساعد في الحفاظ على التكرار إلى minium. يستغرق وظيفة التعيين m :: a -> b ، دالة اختزال r :: (b,a) ->b وإرجاع دالة تقليل جديدة - هذا المُدمج هو في قلب المحولات ؛ إذا كنت مهتمًا ، فقد كتبت إجابات أخرى عنها

// mapReduce = (a -> b, (b,a) -> b, (b,a) -> b)
const mapReduce = (m,r) =>
  (acc,x) => r (acc, m (x))

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.reduce (mapReduce (f, concat), [])

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)
  
// deepFlatten :: [[a]] -> [a]
const deepFlatten =
  concatMap (x =>
    Array.isArray (x) ? deepFlatten (x) : x)

// your sample data
const data =
  [ [ [ 1, 2 ],
      [ 3, 4 ] ],
    [ [ 5, 6 ],
      [ 7, 8 ] ] ]

console.log (flatten (data))
// [ [ 1. 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]

console.log (deepFlatten (data))
// [ 1, 2, 3, 4, 5, 6, 7, 8 ]


حل آخر ECMAScript 6 في نمط وظيفي:

إعلان عن وظيفة:

const flatten = arr => arr.reduce(
  (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
);

واستخدامها:

flatten( [1, [2,3], [4,[5,[6]]]] ) // -> [1,2,3,4,5,6]

طريقة ES6:

const flatten = arr => arr.reduce((acc, next) => acc.concat(Array.isArray(next) ? flatten(next) : next), [])

const a = [1, [2, [3, [4, [5]]]]]
console.log(flatten(a))

طريقة ES5 لتسوية الوظيفة باستخدام ES3 احتياطيًا لمصفوفات N-times المتداخلة:

var flatten = (function() {
  if (!!Array.prototype.reduce && !!Array.isArray) {
    return function(array) {
      return array.reduce(function(prev, next) {
        return prev.concat(Array.isArray(next) ? flatten(next) : next);
      }, []);
    };
  } else {
    return function(array) {
      var arr = [];
      var i = 0;
      var len = array.length;
      var target;

      for (; i < len; i++) {
        target = array[i];
        arr = arr.concat(
          (Object.prototype.toString.call(target) === '[object Array]') ? flatten(target) : target
        );
      }

      return arr;
    };
  }
}());

var a = [1, [2, [3, [4, [5]]]]];
console.log(flatten(a));


فقط الحل الأفضل دون اللجوء

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))

لا تعمل معظم الإجابات هنا على صفائف ضخمة (على سبيل المثال 200000 عنصر) ، وحتى لو كانت كذلك ، فإنها بطيئة. إجابة polkovnikov.ph لديها أفضل أداء ، لكنها لا تعمل من أجل تسطيح عميق.

إليك الحل الأسرع ، والذي يعمل أيضًا في المصفوفات بمستويات متعددة من التعشيش :

const flatten = function(arr, result = []) {
  for (let i = 0, length = arr.length; i < length; i++) {
    const value = arr[i];
    if (Array.isArray(value)) {
      flatten(value, result);
    } else {
      result.push(value);
    }
  }
  return result;
};

أمثلة

صفائف ضخمة

flatten(Array(200000).fill([1]));

انها تتعامل مع صفائف ضخمة على ما يرام. على جهازي يستغرق هذا الرمز حوالي 14 مللي ثانية.

المصفوفات المتداخلة

flatten(Array(2).fill(Array(2).fill(Array(2).fill([1]))));

يعمل مع صفائف متداخلة. ينتج هذا الرمز [1, 1, 1, 1, 1, 1, 1, 1] .

صفائف مع مستويات مختلفة من التعشيش

flatten([1, [1], [[1]]]);

ليس لديها أي مشاكل مع صفائف تسطيح مثل هذا واحد.


لتسوية صفيف من صفائف العناصر المفردة ، لا تحتاج إلى استيراد مكتبة ، الحلقات البسيطة هي الحل الأبسط والأكثر فعالية :

for (var i = 0; i < a.length; i++) {
  a[i] = a[i][0];
}

إلى المحتالين: من فضلك اقرأ السؤال ، لا تفسد نظرًا لأنه لا يناسب مشكلتك المختلفة تمامًا. هذا الحل هو أسرع وأبسط للسؤال المطروح.


ماذا عن استخدام reduce(callback[, initialValue]) طريقة JavaScript 1.8

list.reduce( function( p,n){
    return p.concat( n  );
},[]);

نهج هاسيلسك

function flatArray([x,...xs]){
  return x ? [...Array.isArray(x) ? flatArray(x) : [x], ...flatArray(xs)] : [];
}

var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10];
    fa = flatArray(na);
console.log(fa);


وهنا حل وظيفي بسيط وأداء:

var result = [].concat.apply([], [[1],[2,3],[4]]);
console.log(result); // [ 1, 2, 3, 4 ]

لا فوضى حتمية.


يبدو أن هذا يبدو وكأنه وظيفة لـ RECURSION!

  • يتعامل مع مستويات متعددة من التعشيش
  • يتعامل مع صفائف فارغة ومعلمات غير الصفيف
  • ليس لديه طفرة
  • لا تعتمد على ميزات المتصفح الحديثة

الشفرة:

var flatten = function(toFlatten) {
  var isArray = Object.prototype.toString.call(toFlatten) === '[object Array]';

  if (isArray && toFlatten.length > 0) {
    var head = toFlatten[0];
    var tail = toFlatten.slice(1);

    return flatten(head).concat(flatten(tail));
  } else {
    return [].concat(toFlatten);
  }
};

الاستعمال:

flatten([1,[2,3],4,[[5,6],7]]);
// Result: [1, 2, 3, 4, 5, 6, 7] 

يمكنك استخدام Underscore :

var x = [[1], [2], [3, 4]];

_.flatten(x); // => [1, 2, 3, 4]

يمكنك استخدام concat لدمج المصفوفات:

var arrays = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];
var merged = [].concat.apply([], arrays);

إن استخدام طريقة apply concat سيأخذ فقط المعلمة الثانية كمصفوفة ، لذا فإن السطر الأخير مماثل لذلك:

var merged2 = [].concat(["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]);

يرجى ملاحظة: عندما يتم استخدام Function.prototype.apply ( [].concat.apply([], arrays) أو عامل الانتشار ( [].concat(...arrays) ) من أجل تسوية صفيف ، يمكن تسبب تجاوزات مكدس صفائف كبيرة ، لأنه يتم تخزين كل وسيطة دالة في بنية تخزين العناصر.

في ما يلي تطبيق آمن للكومة في نمط وظيفي يزن أهم المتطلبات مقابل بعضها البعض:

  • إعادة استخدام
  • قراءة
  • الإيجاز
  • أداء

// small, reusable auxiliary functions:

const foldl = f => acc => xs => xs.reduce(uncurry(f), acc); // aka reduce

const uncurry = f => (a, b) => f(a) (b);

const concat = xs => y => xs.concat(y);


// the actual function to flatten an array - a self-explanatory one-line:

const flatten = xs => foldl(concat) ([]) (xs);

// arbitrary array sizes (until the heap blows up :D)

const xs = [[1,2,3],[4,5,6],[7,8,9]];

console.log(flatten(xs));


// Deriving a recursive solution for deeply nested arrays is trivially now


// yet more small, reusable auxiliary functions:

const map = f => xs => xs.map(apply(f));

const apply = f => a => f(a);

const isArray = Array.isArray;


// the derived recursive function:

const flattenr = xs => flatten(map(x => isArray(x) ? flattenr(x) : x) (xs));

const ys = [1,[2,[3,[4,[5],6,],7],8],9];

console.log(flattenr(ys));

بمجرد أن تعتاد على وظائف الأسهم الصغيرة في الشكل المجعد ، وتكوين الوظيفة ووظائف النظام الأعلى ، فإن هذا الرمز يقرأ مثل النثر. ومن ثم ، فإن البرمجة تتكون فقط من تجميع وحدات بناء صغيرة تعمل دائمًا كما هو متوقع ، لأنها لا تحتوي على أي آثار جانبية.


Here's another deep flatten for modern browsers:

function flatten(xs) {
  xs = Array.prototype.concat.apply([], xs);
  return xs.some(Array.isArray) ? flatten(xs) : xs;
};

I recommend a space-efficient generator function :

function* flatten(arr) {
  if (!Array.isArray(arr)) yield arr;
  else for (let el of arr) yield* flatten(el);
}

// Example:
console.log(...flatten([1,[2,[3,[4]]]])); // 1 2 3 4

If desired, create an array of flattened values as follows:

let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]

I'm aware that this is hacky, but the must succinct way I know of to flatten an array(of any depth!) of strings(without commas!) is to turn the array into a string and then split the string on commas:

var myArray =[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]];
var myFlatArray = myArray.toString().split(',');

myFlatArray;
// ["$6", "$12", "$25", "$25", "$18", "$22", "$10", "$0", "$15", "$3", "$75", "$5", "$100", "$7", "$3", "$75", "$5"]

This should work on any depth of nested arrays containing only strings and numbers(integers and floating points) with the caveat that numbers will be converted to strings in the process. This can be solved with a little mapping:

var myArray =[[[1,2],[3,4]],[[5,6],[7,8]],[[9,0]]];
var myFlatArray = myArray.toString().split(',').map(function(e) { return parseInt(e); });
myFlatArray;
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

if your array only consists out of integers or strings you can use this dirty hack:

var arr = [345,2,[34],2,[524,[5456]],[5456]];
var flat = arr.toString().split(',');

Works, in FF, IE and Chrome didn't test the other browsers yet.


const flatten = array => array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []); 

Per request, Breaking down the one line is basically having this.

function flatten(array) {
  // reduce traverses the array and we return the result
  return array.reduce(function(acc, b) {
     // if is an array we use recursion to perform the same operations over the array we found 
     // else we just concat the element to the accumulator
     return acc.concat( Array.isArray(b) ? flatten(b) : b);
  }, []); // we initialize the accumulator on an empty array to collect all the elements
}




flatten