I have an object of filters.
filters = {color: 'black', size: '40'}
i want to return a filtered array of my data. Here's a sample of my data:
data = [
{
id: 1,
name: "Good Engine001"
categories: ['machine'],
color: ['Black', 'white'],
size: [30, 40, 50]
},
{
id: 2,
name: "Good Plane"
categories: ['machine', 'plane'],
color: ['Grey', 'white'],
size: [10, 30, 50]
},
{
id: 3,
name: "Good Chair001"
categories: ['furniture', 'chair'],
color: ['Brown', 'Black'],
size: [3, 5, 40]
}
];
filteredProducts = data.filter((item) =>
Object.entries(filters).every(([key, value]) =>
item[key].includes(value)
)
I'm quite stuck here. I am trying to set the filtered products to be equal to the few entries that matches with the values provided in my filters object. what am i doing wrong?
I was expecting this:
filteredProducts = [
{
id: 1,
name: "Good Engine001"
categories: ['machine'],
color: ['Black', 'white'],
size: [30, 40, 50]
},
{
id: 3,
name: "Good Chair001"
categories: ['furniture', 'chair'],
color: ['Brown', 'Black'],
size: [3, 5, 40]
}
];
But i got the same data.
If you want to compare case-insensitively and loosely compare numbers to their string equivalents, you won't be able to use Array.prototype.includes() since it uses strict comparisons.
Instead, you'll need to use Array.prototype.some() and compare stringified values using base sensitivity...
// Your data with typos fixed and minified
const data = [{"id":1,"name":"Good Engine001","categories":["machine"],"color":["Black","white"],"size":[30,40,50]},{"id":2,"name":"Good Plane","categories":["machine","plane"],"color":["Grey","white"],"size":[10,30,50]},{"id":3,"name":"Good Chair001","categories":["furniture","chair"],"color":["Brown","Black"],"size":[3,5,40]}]
// Stringifies values and compares with "base" sensitivity
const comparator = (dataValue, filterValue) =>
dataValue
.toString()
.localeCompare(filterValue, undefined, { sensitivity: "base" }) === 0;
// Checks various data types using the above comparator
const predicate = (dataValue, filterValue) => {
// Check arrays
if (Array.isArray(dataValue)) {
return dataValue.some((value) => comparator(value, filterValue));
}
// Exclude nested objects and null
if (typeof dataValue !== "object") {
return comparator(dataValue, filterValue);
}
return false;
};
const filters = { color: "black", size: "40" };
const filteredProducts = data.filter((item) =>
Object.entries(filters).every(([key, value]) => predicate(item[key], value))
);
console.log(filteredProducts);
.as-console-wrapper { max-height: 100% !important; }
color names are capital in data while they are not in filter.
'40' is string literal in filter, while it is number in your data.
and also your data should be like this with curly braces around each item:
const data = [
{
id: 1,
name: "Good Engine001",
categories: ['machine'],
color: ['Black', 'white'],
size: [30, 40, 50]
},
{
id: 2,
name: "Good Plane",
categories: ['machine', 'plane'],
color: ['Grey', 'white'],
size: [10, 30, 50]
},
{
id: 3,
name: "Good Chair001",
categories: ['furniture', 'chair'],
color: ['Brown', 'Black'],
size: [3, 5, 40]
}
];
Related
I have a loop that creates arrays like so but I want to find a way for grouping them from the fist values below I have show how the data is and the result I want how do I get the result from the data I have now.
['Brand', 'Workout', 12]
['Colour', 'Pink', 41]
['Fit', 'Regular', 238]
['Size', '10', 21]
['Type', 'T-Shirt', 139]
['Colour', 'Black', 71]
['Brand', 'Matalan', 13]
Brand: {
Workout: 12,
Matalan: 13
},
Colour: {
Pink: 41,
Black: 71
},
Fit: {
Regular: 238
},
Size: {
10: 21
},
Type: {
T-Shirt: 139
}
You can use Array.prototype.reduce for that. What you want to do is, put your input arrays into an array, which creates a two-dimensional array.
After that is done, use Array.prototype.reduce to accumulate an object, and split the input: ['Brand', 'Workout', 12] into the parameters: [key, property, value].
const input = [
['Brand', 'Workout', 12],
['Colour', 'Pink', 41],
['Fit', 'Regular', 238],
['Size', '10', 21],
['Type', 'T-Shirt', 139],
['Colour', 'Black', 71],
['Brand', 'Matalan', 13]
];
const result = input.reduce((accumulator, [key, property, value]) => {
accumulator[key] = {
...(accumulator[key] ?? {}),
[property]: value
};
return accumulator;
}, {});
console.log(result);
I am currently trying to filter available products based on their selected options.
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6
},
{
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5
},
{
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4
}
]
const selectedOptions =
{
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null
}
Typically I would do something like
const availableProducts = products.filter((product) =>
product.horsepowers === selectedOptions.horsepowers &&
product.doors === selectedOptions.doors .... etc
however, how do I skip null values, empty arrays, and undefined values if the user has not yet selected all possible options yet?
The next provided approach takes advantage of the 2nd thisArg argument of almost every available prototypal array method.
Thus one can write a generic filter function which compares any item's property values to the related ones configured by the selectedOptions object which will be passed alongside the filter function as filter's 2nd argument and as the filter function's this context ...
const selectedOptions = {
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null,
};
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6,
}, {
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5,
}, {
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4,
}];
function doItemPropertiesEqualEveryBoundSelectedOption(item) {
return Object
// create key value pairs from the `this` bound selected options.
.entries(this)
// skip/ignore selected option entries where `value` equals `null`.
.filter(([key, value]) => value !== null)
// execute item specific selected option validation via `every`.
.every(([key, value]) => item[key] === value);
}
console.log(
products
.filter(
doItemPropertiesEqualEveryBoundSelectedOption,
selectedOptions,
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
In order to answer another of the OP's questions ...
"however, how do I skip null values, empty arrays, and undefined values if the user has not yet selected all possible options yet?"
... and also provide a generic solution to it, the above approach can be changed to a thisArg object which not only features the selected options but also the condition of not to be validated (invalid) selectedOption properties ...
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6,
}, {
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5,
}, {
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4,
}];
const selectedOptions = {
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null,
};
const isInvalidValue = (value) => {
return Array.isArray(value)
// empty array validation.
? (value.length === 0)
// undefined and null value validation.
: (value == null)
}
function doItemPropertiesEqualEveryBoundValidOption(item) {
const { options, isInvalidValue } = this;
return Object
.entries(options)
.filter(([key, value]) => !isInvalidValue(value))
.every(([key, value]) => item[key] === value);
}
console.log(
products
.filter(
doItemPropertiesEqualEveryBoundValidOption,
{ options: selectedOptions, isInvalidValue },
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
selectedOptions.horsepowers == null ? true : product.horsepowers === selectedOptions.horsepowers &&
product.doors == null ? true : product.doors === selectedOptions.doors
If you want to keep it in line, you can use a ternary operator to check if it's null before comparing.
You could preprocess selectedOptions first and then comparing:
const applicableOptions = Object.entries(selectedOptions).filter(
([_, value]) => value !== null && value !== undefined
);
const availableProducts = products.filter((product) =>
applicableOptions.every(([optKey, optValue]) => product[optKey] === optValue)
);
to compare using array, you would need to update your example as there's no array property
Rather than typing in each option by hand, you could just iterate over selectedOptions. Then it's as simple as checking if the value of each option is null before comparing.
let filtered = products.filter(e => {
for(let [key, value] of Object.entries(selectedOptions))
{
if(value != null && e[key] != value)
return false;
}
return true;
});
const products = [{
id: 1,
name: 'Safari',
horsepowers: 30,
doors: 4,
gear_type: 'automatic',
wheels: 6
},
{
id: 2,
name: 'Jungle',
horsepowers: 50,
doors: 3,
gear_type: 'automatic',
wheels: 5
},
{
id: 3,
name: 'Moon',
horsepowers: 30,
doors: 4,
gear_type: 'manual',
wheels: 4
}
]
const selectedOptions =
{
horsepowers: 50,
doors: 3,
gear_type: null,
wheels: null
}
let filtered = products.filter(e => {
for(let [key, value] of Object.entries(selectedOptions))
{
if(value != null && e[key] != value)
return false;
}
return true;
});
console.log(filtered);
However, if you really want to write it out, I'd just check if the option is set with a simple boolean check. !(null) returns true, so this would work.
return (!selectedOptions.horsepowers || selectedOptions.horsepowers == product.horsepowers) && ...
You could generate a filter array from selectedOptions and filter the entries and use this for filtering the data, later.
const
products = [{ id: 1, name: 'Safari', horsepowers: 30, doors: 4, gear_type: 'automatic', wheels: 6 }, { id: 2, name: 'Jungle', horsepowers: 50, doors: 3, gear_type: 'automatic', wheels: 5 }, { id: 3, name: 'Moon', horsepowers: 30, doors: 4, gear_type: 'manual', wheels: 4 }],
selectedOptions = { horsepowers: 50, doors: 3, gear_type: null, wheels: null },
filter = Object
.entries(selectedOptions)
.filter(([, v]) => v !== null),
result = products.filter(o => filter.every(([k, v]) => o[k] === v));
console.log(result);
Using Object#entries and Array#filter, get the pairs with selected values from selectedOptions to use for filtering the products list
Using Array#filter and Array#every, filter the list to make sure that resulting products match the above pairs
const
products = [ { id: 1, name: 'Safari', horsepowers: 30, doors: 4, gear_type: 'automatic', wheels: 6 }, { id: 2, name: 'Jungle', horsepowers: 50, doors: 3, gear_type: 'automatic', wheels: 5 }, { id: 3, name: 'Moon', horsepowers: 30, doors: 4, gear_type: 'manual', wheels: 4 } ],
selectedOptions = { horsepowers: 50, doors: 3, gear_type: null, wheels: null };
const filterOptions =
Object.entries(selectedOptions).filter(([_, value]) => value !== null);
const selectedProducts =
products.filter(product =>
filterOptions.every(([key, value]) => product[key] === value)
);
console.log(selectedProducts);
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have array of objects where I want to filter and combine results based on specific id. This is example:
[
{
id: 1,
items: [
{
id: 10,
values: [11],
},
{
id: 20,
values: [13, 14, 15],
},
],
},
{
id: 2,
items: [
{
id: 10,
values: [12],
},
{
id: 20,
values: [13, 15],
},
],
},
];
And this is expected result:
[
{
id: 10,
values: [11, 12],
},
{
id: 20,
values: [13, 14, 15],
},
];
I also need to filter duplicates. Thanks
Note: What if I want this result?
[
{
// here I want value for id 10 (it will be always one number)
value: 11,
// here I want values for id 20 (array of numbers) => remove possible duplicates
values: [13, 14, 15],
},
{
// here I want value for id 10 (it will be always one number)
value: 12,
// here I want values for id 20 (array of numbers) => remove possible duplicates
values: [13, 15],
},
];
I tried the same approach with Map, but without success. Basically I want to combine values based on ids.
You could do with Array.flatMap to filter all items in single array.
Then recreate the array with Array.reduce and push the value based on id into new value object
And use Array.filter ignore the duplicate values on array
Object.values return only the value of object in array format
Older
const arr = [ { id: 1, items: [ { id: 10, values: [11], }, { id: 20, values: [13, 14, 15], }, ], }, { id: 2, items: [ { id: 10, values: [12], }, { id: 20, values: [13, 15], }, ], }, ];
const res = Object.values(arr.flatMap(({items})=> items)
.reduce((acc,{id,values})=>{
acc[id] = acc[id] ?? {id,values:[]};
//check the object exist or not
let newArr = acc[id]['values'].concat(values);
let valArr = newArr.filter((v,i)=>newArr.indexOf(v) === i)
//remove the duplicates
acc[id]['values'] = valArr
return acc
},{}))
console.log(res)
Updated
const arr = [ { id: 1, items: [ { id: 10, values: [11], }, { id: 20, values: [13, 14, 15], }, ], }, { id: 2, items: [ { id: 10, values: [12], }, { id: 20, values: [13, 15], }, ], }, ];
function filterMethod(arr,value,values){
return arr.map(({items})=> ({
value:detector(items,value)[0],
values:detector(items,values)
}))
}
function detector(items,idVal){
let ind = items.findIndex(({id})=> id === idVal);
return ind > -1 ? items[ind]['values'] : ['']
}
console.log(filterMethod(arr,10,20))
I have some data I'm trying to transform by filtering by date, however the data can vary in length i.e like this;
longData = [
// These objects could be unlimited but the name key will always be unique
{name: Opt 1,
Data:
// The first value will always be a date string and the second is the amount, the length of this array is always unknown
['2021-01-02', 30],
['2021-01-03, 20],
['2021-02-05', 15]
},
{name: Opt 2,
Data:
['2021-01-02', 30],
['2021-02-08, 20],
['2021-04-02, 15]
},
{name: Opt 3,
Data:
['2021-03-02', 30],
['2021-03-04, 20]
}
What I want to do is essentially create a new object that has the same shape and the original but only includes the data between my dates.
I.e if I have a start date of '2021-01-01' and end of date '2021-01-07'
I should get an object like the following..
shortData = [
{name: Opt 1,
Data:
['2021-01-02', 30],
['2021-01-03, 20]
},
{name: Opt 2,
Data:
['2021-01-02', 30]
},
{name: Opt 3,
Data:
[]
}
I'm trying to break it down by week, these numbers are rendered in a chart and when dealing with large data sets it can break the chart essentially, so I'm trying to page it by week.
Any thoughts on how to accomplish it would be rather helpful.
You can create a function filterByDate which takes your start date and end date strings and converts them into date objects. Then, for each object in longData, you can map it to a transformed object containing a new Data property, which is the filtered version of your Data array.
const longData = [{ name: "Opt 1", Data: [ ['2021-01-02', 30], ['2021-01-03', 20], ['2021-02-05', 15] ] }, { name: "Opt 2", Data: [ ['2021-01-02', 30], ['2021-02-0', 8, 20], ['2021-04-02', 15] ] }, { name: "Opt 3", Data: [ ['2021-03-02', 30], ['2021-03-04', 20] ] } ];
const isBetween = (dx, d1, d2) => d1 < dx && dx < d2;
const filterByDate = (data, start, end) => {
const startDate = new Date(start);
const endDate = new Date(end);
return data.map(obj => ({...obj, Data: obj.Data.filter(
([date]) => isBetween(new Date(date), startDate, endDate)
)}));
}
const res = filterByDate(longData, '2021-01-01', '2021-01-07');
console.log(res);
Given the following array of objects:
const data = [
{
name: "Opt 1",
Data: [
["2021-01-02", 30],
["2021-01-03", 20],
["2021-02-05", 15],
],
},
{
name: "Opt 2",
Data: [
["2021-01-02", 30],
["2021-02-08", 20],
["2021-04-02", 15],
],
},
{
name: "Opt 3",
Data: [
["2021-03-02", 30],
["2021-03-04", 20],
],
},
];
And you simply want to return the same array so that it filters Data property (which is an array of unknown length) by a given date (i.e. "2021-01-02"), I would use Array.prototype.reduce, spread syntax, and Array.prototype.filter to create the new data but with the Data property filtered so that it matches the given date (or in your case, falls within a start & end date):
let newData = data.reduce(
(acc, curr) => [
...acc,
{ ...curr, Data: curr.Data.filter((d) => d[0] === "2021-01-02") },
],
[]
);
// [
// { name: "Opt 1", Data: [["2021-01-02", 30]] },
// { name: "Opt 2", Data: [["2021-01-02", 30]] },
// { name: "Opt 3", Data: [] },
// ];
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have an array of arrays (2d array), and I want to convert it into an array of objects. In the resulting array, I would like each object to have a key-name (see expected output below)
data = [
['Apple', 'Orange', 'Banana', 'Pears', 'Peach'],
[40, 35, 25, 58, 84],
[2, 4, 4, 3, 1, 2],
['Red', 'Yellow', 'Green', 'Violet', 'Blue']
];
My expected result:
expected_result = [
{name: 'Apple', price: 40, quantity: 2, color: 'Red'},
{name: 'Orange', price: 35, quantity: 4, color: 'Yellow'},
{name: 'Banana', price: 25, quantity: 4, color: 'Green'},
{name: 'Pears', price: 58, quantity: 1, color: 'Violet'},
{name: 'Peach', price: 84, quantity: 2, color: 'Blue'}
];
Note The iteration of each array (in data) should be consecutive so that it gives the expected result
Seems to be there is one item extra in the quantity values. I have updated
[2, 4, 4, 3, 1, 2] to [2, 4, 4, 1, 2] removed 3 to match the result, hoping its a typo.
let data = [
["Apple", "Orange", "Banana", "Pears", "Peach"],
[40, 35, 25, 58, 84],
[2, 4, 4, 1, 2],
["Red", "Yellow", "Green", "Violet", "Blue"]
];
let output = [];
let props = ["name", "price", "quantity", "color"];
function updateInfo(row, prop){
row.filter((value, index) => {
if (output[index]) {
output[index][prop] = value;
} else {
output.push({
[prop]: value
});
}
});
};
data.filter((row, index) => {
updateInfo(row, props[index]);
});
console.log(output);
One solution consist of creating first a Map between the outter-array indexes and the property (or key) name you want to assign to them (however this could be replaced by an array like this ["name","price","quantity","color"]). Also, you can obtain the minimun length of the inner arrays to later check for non-creation of objects that won't have all the properties. After you do this pre-initialization, you can use Array.reduce() to generate your expected result:
const data = [
['Apple', 'Orange', 'Banana', 'Pears', 'Peach'],
[40, 35, 25, 58, 84],
[2, 4, 4, 3, 1, 2],
['Red', 'Yellow', 'Green', 'Violet', 'Blue']
];
let mapIdxToProp = new Map([[0, "name"],[1, "price"],[2, "quantity"],[3, "color"]]);
let minLen = Math.min(...data.map(x => x.length));
let res = data.reduce((acc, arr, idx) =>
{
arr.forEach((x, j) =>
{
(j < minLen) && (acc[j] = acc[j] || {}, acc[j][mapIdxToProp.get(idx)] = x);
});
return acc;
}, []);
console.log(res);
The simplest (not most efficient) solution in my opinion is to simply loop through the array, adding to another array as you go.
let arr = [
['Apple', 'Orange', 'Banana', 'Pears', 'Peach'],
[40, 35, 25, 58, 84],
[2, 4, 4, 1, 2],
['Red', 'Yellow', 'Green', 'Violet', 'Blue']
];
let keys = ["name", "price", "quantity", "color"];
let output = [];
//Add's blank objects too the output array
for (let i = 0; i < arr[0].length; i++) {
output.push({});
}
//Loops through the array
for (let i = 0; i < arr.length; i++) {
//Loops through the sub array
for (let x = 0; x < arr[i].length; x++) {
//Adds the sub array to the key that corresponds to it
output[x][keys[i]] = arr[i][x];
}
}
console.log(output);