I have two array:
for example:
arraySelectedItems = [{id: 1, name: "item1"}, {id: 2, name: "item2"}]
arraySavedItems = [{id: 1, name: "item1"}, {id: 2, name: "item2"}]
now I need to check if there is some item in arraySavedItems that is not present in arraySelectedItems, and in this case I'll go to populate another array called arrayDeletedItems.
If the two arrays have the same items I don't need to populate the arrayDeletedItems.
So I have tried with this code:
arraySavedItems.filter((itemSaved) => !arraySelectedItems.find((itemSel) => {
if (itemSaved.id !== itemSel.id) {
arrayDeletedItems.push(itemSaved)
}
}
))
So with this data:
arraySelectedItems = [{id: 1, name: "item1"}, {id: 2, name: "item2"}]
arraySavedItems = [{id: 1, name: "item1"}, {id: 2, name: "item2"}]
I'll expect that arrayDeletedItems will be:
arrayDeletedItems = []
Instead whit this data for example:
arraySelectedItems = [{id: 1, name: "item1"}]
arraySavedItems = [{id: 1, name: "item1"}, {id: 2, name: "item2"}]
I'll expect that arrayDeletedItems will be:
arrayDeletedItems = [{id: 2, name: "item2"}]
With my code I receive and arrayDeletedItems that has the all values:
arrayDeletedItems = [{id: 1, name: "item1"}, {id: 2, name: "item2"}]
Consider this generic function:
function difference(a, b, keyFn) {
let keys = new Set(a.map(keyFn))
return b.filter(obj => !keys.has(keyFn(obj)))
}
//
selectedItems = [{id: 1, name: "item1"}, {id:4}]
savedItems = [{id: 1, name: "item1"}, {id: 2, name: "item2"}, {id:3}, {id:4}]
result = difference(selectedItems, savedItems, obj => obj.id)
console.log(result)
You can use the .includes() method on an array to check whether a value is contained in it (see the documentation for more information).
Now we can just filter the array of saved items to find only ones that aren't contained by the selected items array.
arrayDeletedItems = arraySavedItems.filter((itemSaved) =>
!arraySelectedItems.includes(itemSaved)
)
As #owenizedd points out in the comments, this only works for primitive data types where a shallow equality check is sufficient. A more robust approach can be used with the .reduce() method and a custom equality check. For example, lodash's isEqual() does a deep comparison for equality. You would have to import the module for this. Unfortunately there is no native deep equality check in JavaScript currently (workarounds like JSON.stringify() to then compare the string representations have various downsides).
arrayDeletedItems = arraySavedItems.filter((itemSaved) =>
!arraySelectedItems.reduce((previous, current) =>
previous || _.isEqual(current, itemSaved)
)
)
Note that passing previous as the first argument to the 'or' operator (||) means we can benefit from lazy evaluation - once a hit has been found, the second half of the statement does not need to be evaluated any more.
To solve this problem, since we have id we can utilize it.
You need a key that is unique. so id commonly known will have unique value.
So my approach, find items that is not exist in B array but in A array, and find items that exist in B but not in A array.
This approach not be the fastest, but the findDiff is reusable.
const a = [....];
const b = [....];
const findDiff = (source, target) => {
return source.filter((sourceItem, index) => {
const isInTarget = target.findIndex(targetItem => targetItem.id === sourceItem.id)
return isInTarget === -1
})
}
const difference = findDiff(a,b).concat(findDiff(b,a)); //result
Related
So I have a series of objects that are pulled from an API and inputted into an array, something like such:
array = [
{id: 0, name: "First", relationship: "Friend"},
{id: 1, name: "Second", relationship: "Friend"}
]
The user is allowed to add and remove objects to the list freely (they will appear within a Vue.JS DataTable), and said user is allowed a maximum of 4 objects within the array (lets say 4 "friends")
How should I go about implementing a function that searches the existing array (say, if its populated from the API), and inputs the new object with the corresponding ID that is missing (so if the user deletes the object with the id 2, and adds another, it will search said array with objects, find the missing id 2 slot in the array, and input the object in its place)?
Previously I have gone about it via implement array.find() with conditionals to see if the array contains or does not contain the certain id value, however, it searches through each entry and can end up inserting the same object multiple times. Another method I haven't attempted yet would be having a separate map that contains ids, and then when a user removes an object, having it correspond with the map, and vice versa when adding.
Any suggestions? Thanks
Instead of an array, I'd keep an object in data. Have it keyed by id, like this:
let objects = {
0: { id: 0, name: 'name0', relationship: 'relationship0' },
1: { id: 1, name: 'name1', relationship: 'relationship1' },
}
Integer keys in modern JS will preserve insertion order, so you can think of this object as ordered. The API probably returns an array, so do this...
// in the method that fetches from the api
let arrayFromApi = [...];
this.objects = array.reduce((acc, obj) => {
acc[obj.id] = obj; // insertion order will be preserved
return acc;
}, {});
Your UI probably wants an array, so do this (refer to "array" in the markup):
computed: {
array() {
return Object.values(this.objects);
},
To create a new object, insert it in order, minding the available keys. Note this is a linear search, but with small numbers of objects this will be plenty fast
methods: {
// assumes maxId is const like 4 (or 40, but maybe not 400)
createObject(name, relationship) {
let object = { name, relationship };
for (let i=0; i< maxId; i++) {
if (!this.objects[i]) {
object.id = i;
this.objects[i] = object;
break;
}
}
try this,
let array = [
{id: 0, name: "First", relationship: "Friend"},
{id: 4, name: "Second", relationship: "Friend"},
{id: 2, name: "Second", relationship: "Friend"},
]
const addItem = (item) => {
let prevId = -1
// this is unnecessary if your array is already sorted by id.
// in this example array ids are not sorted. e.g. 0, 4, 2
array.sort((a, b) => a.id - b.id)
//
array.forEach(ob => {
if(ob.id === prevId + 1) prevId++
else return;
})
item = {...item, id: prevId + 1 }
array.splice(prevId+1, 0, item)
}
addItem({name: "x", relationship: "y"})
addItem({name: "a", relationship: "b"})
addItem({name: "c", relationship: "d"})
console.log(array)
You can simply achieve this with the help of Array.find() method along with the Array.indexOf() and Array.splice().
Live Demo :
// Input array of objects (coming from API) and suppose user deleted 2nd id object from the array.
const arr = [
{id: 0, name: "First", relationship: "Friend" },
{id: 1, name: "Second", relationship: "Friend" },
{id: 3, name: "Fourth", relationship: "Friend" }
];
// find the objects next to missing object.
const res = arr.find((obj, index) => obj.id !== index);
// find the index where we have to input the new object.
const index = arr.indexOf(res);
// New object user want to insert
const newObj = {
id: index,
name: "Third",
relationship: "Friend"
}
// Insert the new object into an array at the missing position.
arr.splice(index, 0, newObj);
// Output
console.log(arr);
Right now I need to merge the string into one.
This is my try ->
After this merge i got example of array ->
[
[{id: 1, name: "One"}],
[{id: 2, name : "two"}]
]
problem is newData because always print new array.
I need to data be like ->
[
{id: 1, name: "One"},
{id: 2, name : "two"}
]
What i am try, with foreEach ->
newState((oldData) => [...oldData, newData.forEach((new) => new)]);
No work.
Also what I am try
let filteredArray = newData.map(data => data);
Also no work, why?
Every time I get new information inside array newData....
I need solution to get only result inside array and print to
newState((oldData) => [...oldData, newResultWhichIsObject]);
Also some time my newData have few object inside array
The map method isn't the right method to use in your case. Map method will take as an entry an array of n elements and mutate it into an array of n element, on which an operation was applied. See MDN documentation
You should use the reduce method, which provides the ability to construct a brand new array from an empty one, here is the snippet :
const baseArray = [
[{id: 1, name: "One"}],
[{id: 2, name : "two"}]
];
const flattenedArray = baseArray.reduce((acc, curr) => ([...acc, ...curr]), []);
// For demo purpose, console.log
console.log(flattenedArray);
Reduce array method is a bit tricky, that is why I invite you to read the documentation carefully and play with it.
You can use .flat() method. Try this
const newData = [
[{id: 1, name: "One"}],
[{id: 2, name : "two"}]
];
console.log(newData.flat())
I wanted to add a key:value parameter to all the objects in an array from another array
eg:
var arrOfObj = [{id: 001, date:'22/05/2020', Actor:'jane'},
{id: 002, date:'02/03/2020', Actor:'alice'},
{id: 003, date:'11/06/2020', Actor:'jean'},
{id: 004, date:'20/01/2020', Actor:'yann'}];
var arrayScore = [44,2,3,5];
I want add for every objects a key:value parameter from arrayScore, like :
var arrOfObj = [{id: 001, date:'22/05/2020', Actor:'jane', score:44},
{id: 002, date:'02/03/2020', Actor:'alice', score:2},
{id: 003, date:'11/06/2020', Actor:'jean', score:3},
{id: 004, date:'20/01/2020', Actor:'yann', score:5}];
I tried this code:
var result = arrOfObj.map(function(el) {
var o = Object.assign({}, el);
o.score = arrayScore;
return o;
});
console.log(result);
but arrOfObj add all values from arrayScore for every object!!
How can I change this please??
Thank you for your HELP!
You can use Array.map to create the new array including the user scores, I would also take note of TJCrowders's point about the Ids.
var arrOfObj = [{id: 1, date:'22/05/2020', Actor:'jane'},
{id: 2, date:'02/03/2020', Actor:'alice'},
{id: 3, date:'11/06/2020', Actor:'jean'},
{id: 4, date:'20/01/2020', Actor:'yann'}];
var arrayScore = [44,2,3,5];
const result = arrOfObj.map((el, index) => ({...el, score: arrayScore[index] }));
console.log("Result with scores:", result);
Since you do not need a new array of objects, but only need to add the properties to the objects in the array, you can use the array method forEach instead of map.
If we pass two parameters to the callback provided to forEach, the second parameter will receive the index of the array element we are iterating over. This allows us to assign the corresponding value from the arrayScore array.
This should work
arrOfObj.forEach((o, i) => {
o.score = arrayScore[i];
});
Cheers!
I have two arrays like so
data = [{id: 1, name: apple},
{id: 2, name: mango},
{id: 3, name: grapes},
{id: 4, name: banana}]
data2 =[{id: 1, name: apple},
{id: 3, name grapes}]
My Expected result would be:
[{ id: 2, name: mango},
{id:4, name: banana}]
My code is
let finalData =[];
data.forEach(result => {
data2.find(datum => {
if(datum['id'] === result['id]{
finalData.push(result);
}
})
})
I am getting wrong result. What is the simplest code or library that I can use?
Your sample data doesn't make sense, but assuming you mean that all data items that have matching IDs also have matching names and also assuming you want a set of all items where the IDs are the same in the two sets of data, you could use a Set to keep track of which IDs are present in one array then filter the second array by those that have their IDs in the set:
const idsInFirst = new Set(data.map(d => d.id));
const intersection = data2.filter(d => idsInFirst.has(d.id));
The reason why an intermediate Set structure is used is because it allows O(1) lookups after a one-time scan, which is more efficient than repeatedly scanning the first array over and over.
If you meant to say you wanted a difference between data sets (items excluded from data that are in data2), you'd want to negate/inverse things a bit:
const idsToExclude = new Set(data2.map(d => d.id));
const difference = data.filter(d => !idsToExclude.has(d.id));
Edit
After your clarifying edit, it's that second block of code that you'll want.
I would say a good way to do that is filtering your longest array using a function that will validate if the object id is present in both arrays. Check this example:
const data = [
{id: 1, name: 'apple'},
{id: 2, name: 'mango'},
{id: 3, name: 'grapes'},
{id: 4, name: 'banana'}
]
const data2 =[
{id: 1, name: 'apple' },
{id: 3, name: 'grapes' }
]
const longest = data.length > data2.length ? data : data2;
const shortest = data.length <= data2.length ? data : data2;
const finalData = longest.filter( obj => !shortest.find( o => o.id === obj.id ) )
console.log(finalData)
Good luck!
This question already has answers here:
javascript filter array of objects
(8 answers)
Closed 7 years ago.
I have the following array of objects :
var objs = [{id: 1, name: 'foo', ...},
{id: 2, name: 'bar', ...},
{id: 3, name: 'baz', ...}];
and this variable :
var matcher = 'bar';
What is the easiest way to get the object that has the matcher equals to its name ?
Using this matcher the result should be :
{id: 2, name: 'bar', ...}
The easiest way is to use filter function
var objs = [{id: 1, name: 'foo'},
{id: 2, name: 'bar'},
{id: 3, name: 'baz'}];
var matcher = 'bar';
var result = objs.filter(function(obj) {
return obj.name === matcher;
});
console.log(result); // [{id: 2, name: 'bar'}]
console.log(result[0]); // {id: 2, name: 'bar'}
The easiest way is probably using a library which allows some higher level functions on objects, such as filter.
Such libraries are for example underscore and lodash, both reserving the _ sign for use, with which you'd write:
_(objs).filter(function(element){return element.name === matcher})[0];
(The filter function returns the elements of an array or objects for which the given filtering function returns true - in this case, you're checking whether the name equals your matcher. The return value is an array of these elements, hence the [0] part)
EDIT: I didn't notice you had an array to begin with, then you don't even need an external library, arrays by default have the filter method on them.