learningMonk
learningMonk

Reputation: 1401

split an array into two based on condition using javascript

I have an array of objects like this below.

[
    {
        product_id: 4,
        product_name: "Samsung",
        category_name: "Tv and home appliance",
        is_Available: 1
    },
    {
        product_id: 8,
        product_name: "Apple",
        category_name: "Home gadgets",
        is_Available: 1
    },
    {
        product_id: 9,
        product_name: "Verifone",
        category_name: "Electronics",
        is_Available: 0
    }
  ]

I want to split this array into two based on is_Available flag value. So i did like this using reduce.

const formmattedResponse = data.reduce((arr,el) => {
            if(el.is_Available === 1) {
                arr.push({...el});
            }
            return arr;
        },[]);

But, i need this type of formatted data like below based on above data array

{
    availableData: [{
        product_id: 4,
        product_name: "Samsung",
        category_name: "Tv and home appliance",
        is_Available: 1
    },
    {
        product_id: 8,
        product_name: "Apple",
        category_name: "Home gadgets",
        is_Available: 1
    }
   ],
  notAvailableData: [{
        product_id: 9,
        product_name: "Verifone",
        category_name: "Electronics",
        is_Available: 0
    }
   ]
  }

Upvotes: 9

Views: 14998

Answers (8)

Giorgio Tempesta
Giorgio Tempesta

Reputation: 2059

I've seen a lot of examples using Array.reduce(), which has the advantage of doing all the computation in a single loop, but I've found a solution that in my opinion is easier to read, even if internally it requires two loops instead of one.

I started from a native implementation of the lodash method called difference(), as you can find in the great You Might Not Need Lodash page.

const difference = (arr1, arr2) => arr1.filter(x => !arr2.includes(x))

Once I was able to create a new array from the values of the original array that are not in the filtered one, the implementation became very easy.

// filter the values that satisfy the condition
const firstResult = originalArray.filter(q => q.is_Available === 1);
// compute the rest by using the difference() method
const secondResult = difference(originalArray, firstResult);

At this point the whole logic can be extracted in a new partition() method.

// import difference, or declare it before partition
const partition =  (arr, condition) => {
    const arr1 = arr.filter(condition);
    const arr2 = difference(arr, arr1);
    return [arr1, arr2];
};

And here is a full snippet on how to make it work with your data

const testableArray = [
  {
    product_id: 4,
    product_name: 'Samsung',
    category_name: 'Tv and home appliance',
    is_Available: 1,
  },
  {
    product_id: 8,
    product_name: 'Apple',
    category_name: 'Home gadgets',
    is_Available: 1,
  },
  {
    product_id: 9,
    product_name: 'Verifone',
    category_name: 'Electronics',
    is_Available: 0,
  },
];

const [arrayOne, arrayTwo] = partition(testableArray, q => q.is_Available === 1);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386600

UPDATE 2024

With Object.groupBy, it goes shorter by taking a group value.

const
    data = [{ product_id: 4, product_name: "Samsung", category_name: "Tv and home appliance", is_Available: 1 }, { product_id: 8, product_name: "Apple", category_name: "Home gadgets", is_Available: 1 }, { product_id: 9, product_name: "Verifone", category_name: "Electronics", is_Available: 0 }],
    result = Object.groupBy(data, ({ is_Available }) => is_Available
        ? 'availableData'
        : 'notAvailableData'
    );

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

You could take an array and push the objects according their availability with a single loop.

const
    data = [{ product_id: 4, product_name: "Samsung", category_name: "Tv and home appliance", is_Available: 1 }, { product_id: 8, product_name: "Apple", category_name: "Home gadgets", is_Available: 1 }, { product_id: 9, product_name: "Verifone", category_name: "Electronics", is_Available: 0 }],
    result = data.reduce((r, o) => {
        r[o.is_Available ? 'availableData' : 'notAvailableData'].push(o);
        return r;
    }, { availableData: [], notAvailableData: [] });

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

Upvotes: 19

Alexandre Martin
Alexandre Martin

Reputation: 59

If not using lodash, may I suggest this :

var arr = [
  {
    product_id: 4,
    is_Available: 1
  },
  {
    product_id: 8,
    is_Available: 1
  },
  {
    product_id: 9,
    is_Available: 0
  }
]

const res = {notAvailableData: []}
res.availableData = arr.filter((a) => a.is_Available || !res.notAvailableData.push(a))

console.log(res)

.push() returns the new length of the array, so ![].push shouldn't ever be true

Upvotes: 0

Oskari Mantere
Oskari Mantere

Reputation: 302

The lodash partition function was designed to solve this problem:

import { partition } from "lodash";
const [availableData, notAvailableData] = partition(items, "is_Available");
const formattedResponse = { availableData, notAvailableData };

where items is the array of objects given in the question.

Upvotes: 3

Ben Stephens
Ben Stephens

Reputation: 3371

I believe that you can find partition functions in some libraries e.g. lodash that will split an array into two arrays, one that matches the condition and another that doesn't. I've written a quick version here to demo it.

const data = [{"product_id":4,"product_name":"Samsung","category_name":"Tv and home appliance","is_Available":1},{"product_id":8,"product_name":"Apple","category_name":"Home gadgets","is_Available":1},{"product_id":9,"product_name":"Verifone","category_name":"Electronics","is_Available":0}];

const partition = (array, filter_fn) =>
  array.reduce(
    (acc, val) => (acc[filter_fn(val) ? 0 : 1].push(val), acc),
    [[], []]
  );

const new_keys = ['availableData', 'notAvailableData'];

const result = Object.fromEntries(
  partition(data, (item) => item.is_Available)
    .map((arr, i) => [new_keys[i], arr])
);

console.log(result);

Upvotes: 1

Jeffrey Devloo
Jeffrey Devloo

Reputation: 1426

You have two options: you either filter the list twice or you can add a different accumulator for your reduce function which holds two arrays.

This advantage of the reduce is that only iterate your array once, making it more efficient than the double filter.

I pass a object with 2 properties: availableData and notAvailableData as the initial value for the reduce. During the reduce, I check the property that you want to filter on and add it to the right list. In the reduce function, I return the context object so the next iteration has it also.

The reduce returns that object in the end, completing the sort.

var arr = [
  {
    product_id: 4,
    product_name: "Samsung",
    category_name: "Tv and home appliance",
    is_Available: 1
  },
  {
    product_id: 8,
    product_name: "Apple",
    category_name: "Home gadgets",
    is_Available: 1
  },
  {
    product_id: 9,
    product_name: "Verifone",
    category_name: "Electronics",
    is_Available: 0
  }
]

var context = arr.reduce((ctx, el) => {
  if (el.is_Available) {
    ctx.availableData.push(el);
  } else {
    ctx.notAvailableData.push(el);
  }
  return ctx
}, {availableData: [], notAvailableData: []})

console.log(context);

Upvotes: 1

Sudarshan
Sudarshan

Reputation: 814

modified Snippet could be like


const formmattedResponse = data.reduce((arr,el) => {
          let finalResp = { 
                  availableData: [],
                notAvailableData : []
            }
            if(el.is_Available === 1) {
                finalResp["availableData"].push({...el});
            } else {
        finalResp["notAvailableData "].push({...el});
     }
            return finalResp;
        },[]);


Upvotes: 0

Nicolae Maties
Nicolae Maties

Reputation: 2665

const data = [
    {
        product_id: 4,
        product_name: "Samsung",
        category_name: "Tv and home appliance",
        is_Available: 1
    },
    {
        product_id: 8,
        product_name: "Apple",
        category_name: "Home gadgets",
        is_Available: 1
    },
    {
        product_id: 9,
        product_name: "Verifone",
        category_name: "Electronics",
        is_Available: 0
    }
  ];
const result = {
  availableData:  data.filter(el => el.is_Available),
  notAvailableData:  data.filter(el => !el.is_Available),
};

console.log(result);

You can use Array.filter() to filter your array based on is_Available.

Upvotes: 2

Related Questions