Check if ID Is Found Another Array in ES6 - javascript

I wanted to filter out the data. I wanted to check if data on data1 is found on data2 and to check if it has errorMessages. Please check my code below. Is there a better way to do it?
data1
[
{
"ids": "0111",
},
{
"ids": "0222",
},
{
"ids": "0333",
}
]
data2
[
{
"id": "0111",
"errorMessages": [
{
"message": ["sample error message 1"]
}
]
},
{
"id": "0333",
"errorMessages": []
}
]
Code
const output= data1.filter(
(element) => element.ids === data2.find((data) => data).id
);
console.log("output", output);

.find((data) => data) doesn't do anything useful - each item in the array is an object, which is truthy, so that'll always return the first element in the array.
If you did want to .find a matching element in the other array - then a better approach would be to make a Set of the IDs found in the other array first (Set lookup is much quicker - O(1) - than .find, which is O(n)).
You also need to implement the logic to check if the errorMessages is empty.
const data1 = [
{
"ids": "0111",
},
{
"ids": "0222",
},
{
"ids": "0333",
}
]
const data2 = [
{
"id": "0111",
"errorMessages": [
{
"message": ["sample error message 1"]
}
]
},
{
"id": "0333",
"errorMessages": []
}
]
const ids = new Set(
data2
.filter(item => item?.errorMessages.length)
.map(item => item.id)
);
const output= data1.filter(
element => ids.has(element.ids)
);
console.log("output", output);

Without Set, but use Object as the map.
const IDKeys = {};
data2.forEach(data => {
if (data.errorMessages.length){
IDKeys[data.id] = true; // means valid
}
})
const filteredArray = data1.filter(data => IDKeys[data.id]);
This would be only O(n) since accessing key on object is O(1)

Related

How to remove object element that match from an array using splice?

here is my data of array1 :
[ { members: [ '60ee9148104cc81bec3b97ab' ] } ]
and here is array2:
[{"_id": "60ee9148104cc81bec3b97ab","username": "user1", "email": "user1#gmail.com"}, {"_id": "60ee917f767bd11d687326c7","username": "user2","email": "user2#gmail.com"}]
and I want to remove the object from my array2 which _id is equal to 60ee9148104cc81bec3b97ab
I have tried so far as
let user = await User.find({ _id: { $ne: req.user._id } })
const getNonfriends = (one) => {
user.splice(user.indexOf(one.members[0]), 1)
//user.filter(entry => entry._id !== one.members[0])
}
array1.map(getNonfriends)
filter or splice non of them bring my solutions.
The mistake you make is that you have similar objects, yet you search for identical objects.
var array1 = [ { members: [ '60ee9148104cc81bec3b97ab' ] } ];
var array2 = [{"_id": "60ee9148104cc81bec3b97ab","username": "user1", "email": "user1#gmail.com"}, {"_id": "60ee917f767bd11d687326c7","username": "user2","email": "user2#gmail.com"}]
for (var x of array1) {
for (var member of x.members) {
var objects = array2.filter(item => item._id === member)
for (var obj of objects) array2.splice(array2.indexOf(obj), 1)
}
}
We can use the Array.prototype.filter()
I think there is no need to find the index of the array and slice.
Also, we can use the Array.prototype.map, it is similar to use the filter function
const obj1 = [{
members: ['60ee9148104cc81bec3b97ab']
}]
const obj2 = [{
"_id": "60ee9148104cc81bec3b97ab",
"username": "user1",
"email": "user1#gmail.com"
}, {
"_id": "60ee917f767bd11d687326c7",
"username": "user2",
"email": "user2#gmail.com"
}]
const getAnswer = (obj2) => {
const res = obj2.filter(item => !obj1[0].members.includes(item._id))
return res;
}
console.log(getAnswer(obj2));
As you are working with IDs, I am going to assume that the second array cannot contain two items with the same ID.
If that assumption is correct, you could do:
const data = [{
"_id": "60ee9148104cc81bec3b97ab",
"username": "user1",
"email": "user1#gmail.com"
}, {
"_id": "60ee917f767bd11d687326c7",
"username": "user2",
"email": "user2#gmail.com"
}];
const filter = [{
members: ['60ee9148104cc81bec3b97ab']
}];
// SOLUTION 1: using Array.prototype.findIndex() and Array.prototype.splice()
const filteredDataA = [...data] // as splice() modifies the original array, I think it is safer to work on a copy of the original data
filter[0].members.forEach(id => {
const index = filteredDataA.findIndex(item => item._id === id); // find the index of the item with the same ID
if (index > -1) filteredDataA.splice(index, 1); // (if found) remove the item
})
console.log('SOLUTION 1: using Array.prototype.findIndex() and Array.prototype.splice()');
console.log(filteredDataA);
console.log('--------------------');
// SOLUTION 2: using array.prototype.filter() and array.prototype.includes()
const filteredDataB = data.filter(item => !filter[0].members.includes(item._id));
console.log('SOLUTION 2: using array.prototype.filter() and array.prototype.includes()');
console.log(filteredDataB);
console.log('--------------------');
I'd prefer "Solution 2" as I think is more readable
From the answers, I got the clue and this brings my desire results. here is one thing that array1 can have multiple elements so it needs to be mapped over with invoking the getNonfriends finction.
so,
let user = await User.find({ _id: { $ne: req.user._id } })
const getNonfriends = (one) => {
user = user.filter(item => !one.members.includes(item._id))
return user;
}
await array1.map(getNonfriends)

JavaScript add json to existing object

I'm struggling with adding JSON to existing objects with Vue.js.
So I have a function that is calculating some variants and it's working well for now, but I would need to change them in order to give correct data to my Laravel backend.
So this is how my array looks like (this array is used to calculate variants)
"attributes":[
{
"title":{
"text":"Size",
"value":"1"
},
"values":[
"Red",
"Green"
]
},
{
"title":{
"text":"Color",
"value":"2"
},
"values":[
"X",
"M"
]
}
]
And I'm calculating them like this:
addVariant: function () {
const parts = this.form.attributes.map(attribute => attribute.values);
const combinations = parts.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
this.form.variants = combinations;
},
And the output looks like:
"variants":[
[
"Red",
"X"
],
[
"Red",
"M"
],
[
"Green",
"X"
],
[
"Green",
"M"
]
]
So this is calculated well, but this variants would need to look like this:
"variants":[
{
"title": "Red/M",
"slug": "prooooo",
"options": [
{
"key": "Color",
"value": "Red"
},
{
"key": "Size",
"value": "M"
}
]
},
{
"title": "Red/X",
"slug": "prooooo",
"options": [
{
"key": "Color",
"value": "Red"
},
{
"key": "Size",
"value": "X"
}
]
}
As you see I have a title field that is calculated by options.values, but the real question is how to make that JSON look like the last given JSON.
Any ideas on how to do it?
From what I see, the missing values to create the final output are the key properties of the options arrays, which I see in the first attributes array under the text property of the title object.
The first change that you can do is on the creation of the parts variable: instead of returning the attribute.values directly, map over them to save the attribute.title.text as well.
At the same time, we set the keys of the returned object to match the options object from the desired output.
const parts = this.form.attributes.map((attribute) =>
attribute.values.map((value) => {
// `options` objects have `value` and `key` properties
return { value, key: attribute.title.text };
})
);
The combinations code remains the same.
Then, we loop over the combinations and create the new variants array
let variants = [];
for (const combination of combinations) {
variants.push({
// use template strings to create the new title
title: `${combination[0].value}/${combination[1].value}`,
slug: "...the value that you want",
// the "combination" variable already has the structure that you want for "options"
options: combination
});
}
The final code will look something like this:
addVariant: function () {
const parts = this.form.attributes.map((attribute) =>
attribute.values.map((value) => {
return { value, key: attribute.title.text };
})
);
const combinations = parts.reduce((a, b) =>
a.reduce((r, v) => r.concat(b.map((w) => [].concat(v, w))), [])
);
let variants = [];
for (const combination of combinations) {
variants.push({
title: `${combination[0].value}/${combination[1].value}`,
slug: "...the value that you want",
options: combination,
});
}
this.variants = variants;
}

Logic to Transform data

I have an api that return me data in following format:
[
{
"_id": 1567362600000,
"KIDate": "2019-09-02",
"KITools": [
{
"data": 1,
"tool": "A"
},
{
"data": 2,
"tool": "B"
}
]
},
{
"_id": 1567519839316,
"KIDate": "2019-09-01",
"KITools": [
{
"data": 2,
"tool": "A"
},
{
"data": 1,
"tool": "C"
}
]
},
{
"_id": 1567519839317,
"KIDate": "2019-08-31",
"KITools": [
{
"data": 0,
"tool": "C"
}
]
},
]
I want to transform this data to get the following arrays:
Result 1 - [“2019-09-02”,”2019-09-01”,”2019-08-31”]
Result 2 - [ {name: ‘A’, data:[1, 2, 0] }, { name: 'B', data: [2, 0, 0] }, { name: 'C', data: [0, 1, 0]}]
Currently I am able to achieve this by using loops and per-defining variables with the tool name like following and looping the api data to push into this variable.
var result2 = [{
name: 'A',
data: []
}, {
name: 'B',
data: []
}, {
name: 'C',
data: []
}];
But this is not the expected behavior, the tool names can change and I have to figure that out dynamically based on the data returned by the api.
What is the best way to achieve this without looping like crazy.
You could use reduce method to get the result with array of dates and object of values for each tool.
const data = [{"_id":1567362600000,"KIDate":"2019-09-02","KITools":[{"data":1,"tool":"A"},{"data":2,"tool":"B"}]},{"_id":1567519839316,"KIDate":"2019-09-01","KITools":[{"data":2,"tool":"A"},{"data":1,"tool":"C"}]},{"_id":1567519839317,"KIDate":"2019-08-31","KITools":[{"data":0,"tool":"C"}]}]
const result = data.reduce((r, {KIDate, KITools}, i) => {
r.dates.push(KIDate);
KITools.forEach(({data: dt, tool}) => {
if(!r.values[tool]) r.values[tool] = Array(data.length).fill(0);
r.values[tool][i] = dt
})
return r;
}, {dates: [], values: {}})
console.log(result)
You can use reduce and forEach with Set and Map
Initialize accumulator as object with dates and data key, dates is a Set and data is Map
For every element add the KIDate to dates key,
Loop over KITools, check if that particular too exists in data Map if it exists update it's value by adding current values to id, if not set it's value as per current values
let data = [{"_id": 1567362600000,"KIDate": "2019-09-02","KITools": [{"data": 1,"tool": "A"},{"data": 2,"tool": "B"}]},{"_id": 1567519839316,"KIDate": "2019-09-01","KITools": [{"data": 2,"tool": "A"},{"data": 1,"tool": "C"}]},{"_id": 1567519839317,"KIDate": "2019-08-31","KITools": [{"data": 0,"tool": "C"}]},]
let final = data.reduce((op,{KIDate,KITools})=>{
op.dates.add(KIDate)
KITools.forEach(({data,tool})=>{
if(op.data.has(data)){
op.data.get(data).data.push(tool)
} else{
op.data.set(data, {name: data, data:[tool]})
}
})
return op
},{dates:new Set(),data: new Map()})
console.log([...final.dates.values()])
console.log([...final.data.values()])
The result1 array can be obtained via a direct .map(). To build the result2 array will require additional work - one approach would be to do so via .reduce() as detailed below:
const data=[{"_id":1567362600000,"KIDate":"2019-09-02","KITools":[{"data":1,"tool":"A"},{"data":2,"tool":"B"}]},{"_id":1567519839316,"KIDate":"2019-09-01","KITools":[{"data":2,"tool":"A"},{"data":1,"tool":"C"}]},{"_id":1567519839317,"KIDate":"2019-08-31","KITools":[{"data":0,"tool":"C"}]}];
const result1 = data.map(item => item.KIDate);
const result2 = data.reduce((result, item) => {
item.KITools.forEach(kitool => {
/* For current item, search for matching tool on name/tool fields */
let foundTool = result.find(i => i.name === kitool.tool);
if (foundTool) {
/* Add data to data sub array if match found */
foundTool.data.push(kitool.data);
} else {
/* Add new tool if no match found and init name and data array */
result.push({
name: kitool.tool,
data: [kitool.data]
});
}
});
return result;
}, []).map((item, i, arr) => {
/* Second phase of processing here to pad the data arrays with 0 values
if needed */
for (let i = item.data.length; i < arr.length; i++) {
item.data.push(0);
}
return item;
});
console.log('result1:', result1);
console.log('result2:', result2);

Filter Sub Array of Array

I am trying to filter a sub array of a parent array and my results are coming back empty. I am trying to find matches to the color_family.
My array looks like:
const arr =[
{
"id": 123,
"acf": {
"product_colors": [
{
"color_family": "grey"
},
{
"color_family": "taupe"
}
]
}
},
{
"id": 456,
"acf": {
"product_colors": [
{
"color_family": "red"
},
{
"color_family": "taupe"
}
]
}
}
]
What I am filtering on is
const findColors = ["grey", "taupe"]
What I have tried with no luck is
const res = arr.filter(
x => x.acf.product_colors.find(
color_family => findColors.includes(color_family)
)
)
This is returning no results when it should return 2 results. Can someone point me in the right direction?
In addition to the typo, the param to the find argument is an object with color_family:
const res = arr.filter(x => x.acf.product_colors.find(col => {
return findColors.includes(col.color_family);
}))

lodash filter not finding value in multidimensional array of Shopify order

Looking to see if an order line_item has been refunded before processing...
Here is a single order:
var order = {
line_items: [
{
id: 1326167752753
}
],
refunds: [
{
refund_line_items: [
{
id: 41264152625,
line_item_id: 1326167752753,
}
]
}
]
};
Trying to log out the filter results:
console.log(
_.filter(order, {
refunds: [
{
refund_line_items: [
{
line_item_id: 1326167752753
}
]
}
]
}).length
);
I'm getting 0 on the console.
Am I using _.filter wrong in this case?
Function take needs an array (order is not an array, order.refunds is) and a predicate, not an object.
Anyway, I'd write it using Array.some:
const itemWasRefunded = order.refunds.some(refund =>
refund.refund_line_items.some(refund_line_item =>
refund_line_item.line_item_id === 1326167752753
)
);
Or, alternatively, getting all line_item_ids and checking inclusion:
const itemWasRefunded = _(order.refunds)
.flatMap("refund_line_items")
.map("line_item_id")
.includes(1326167752753);
You can use some and find and do this in lodash and also easily in ES6:
var order = { line_items: [{ id: 1326167752753 }], refunds: [{ refund_line_items: [{ id: 41264152625, line_item_id: 1326167752753, }] }] };
// lodash
const _searchRefunds = (lid) => _.some(order.refunds, x =>
_.find(x.refund_line_items, {line_item_id: lid}))
console.log('loadsh:', _searchRefunds(1326167752753)) // true
console.log('loadsh:', _searchRefunds(132616772323232352753)) // false
//es6
const searchRefunds = (lid) => order.refunds.some(x =>
x.refund_line_items.find(y => y.line_item_id == lid))
console.log('ES6:', searchRefunds(1326167752753)) // true
console.log('ES6:', searchRefunds(132616772323232352753)) // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Categories

Resources