Compare between two arrays - javascript

While I was thinking in someway to compare between two arrays, accidentally for the first time something like this happens with me.. something worked with me for the first time without showing me any errors!
This is a very simplified visualization for an application I'm working on currently.
I have two arrays, one called options, the other is called correct.
so the options are options of some question, the chances are the chances of this question.
in this example two of the options are correct answers..
so what I have to do is to compare between this two arrays to check if it returns true (later) or if it's returning false (not included in the code).
could you please explain for me how this actually worked?
const options = ['facebook', 'twitter', 'tango', 'skype'];
const correct = ['twitter', 'skype'];
const trueIndexes = [];
options.forEach((cur, index) => {
correct.forEach((cur1, index1) => {
if (cur === cur1) {
trueIndexes.push(index);
}
});
});
console.log(trueIndexes);

There are nested forEach loops for each array, and during each inner loop there is a conditional test to ascertain if one element exists in the other. If so, the index of where the matching item exists in the other array is pushed to the new trueIndexes array.
Another way to write this is:
const options = ['facebook', 'twitter', 'tango', 'skype']
const correct = ['twitter', 'skype']
const trueIndices = correct.reduce((_trueIndices, correctItem) => {
let correctItemIndex = options.indexOf(correctItem)
if(correctItemIndex !== -1) _trueIndices.push(correctItemIndex)
return _trueIndices
}, [])
console.log(trueIndices)
or
const options = ['facebook', 'twitter', 'tango', 'skype']
const correct = ['twitter', 'skype']
const trueIndices = []
correct.forEach((correctItem) => {
let correctItemIndex = options.indexOf(correctItem)
if(correctItem !== -1) trueIndices.push(correctItem)
})
console.log(trueIndices)
Both of these alternate solutions should be faster.

Your code goes through each option and compares it to both correct values.
If they match the index of the option is added to the true indexes list.
So the comparisons are like follows:
'facebook' = 'twitter'? no
'facebook' = 'skype'? no
'twitter' = 'twitter'? YES -> add index to list
'twitter' = 'skype'? no
'tango' = 'twitter'? no
'tango' = 'skype'? no
'skype' = 'twitter'? no
'skype' = 'skype'? YES -> add index to list

As explained by others, you just iterate over all the combinations of these 2 arrays and collect the indexes where an option is in both arrays(in other words, if an option is true)
I just wanted to add that you should perhaps change your data structure so that options are stored with the questions...and correct answers are indicated by a flag so they don't need to be looked up.
For example:
const questions = [
{
id: 'social_1',
question: 'Which of these are popular social media sites?',
answers: [
{id: 'fb', label: 'Facebook', correct: true},
{id: 'skype', label: 'Skype', correct: false},
{id: 'twitter', label: 'Twitter', correct: true},
{id: 'yt', label: 'YouTube', correct: false},
]
}, {
id: 'cars_1',
question: 'Which of these are car brands?',
answers: [
{id: 'tesla', label: 'Tesla', correct: true},
{id: 'bmw', label: 'BMW', correct: true},
{id: 'twitter', label: 'Twitter', correct: false},
{id: 'yt', label: 'YouTube', correct: false},
]
}
];
https://jsfiddle.net/rainerpl/L438qjms/26/

Related

How do I filter an array while excluding specific conditions?

I have button that I’m disabling by reading an array and checking its properties.
My array looks like this:
const arr = [
{id: ‘1’, name: ‘SOME_THING’, status: ‘AGREED’},
{id: ‘2’, name: ‘THIS_THING’, status: ‘AGREED’},
{id: ‘3’, name: ‘OTHER_THING’, status: ‘AGREED’},
{id: ‘4’, name: ‘EVERY_THING’, status: ‘AGREED’},
];
My current logic to disable/enable this button looks like this:
arr.filter((item) => item.indexOf(‘THING’) > -1)
.every((item) => item.status === ‘AGREED’)
Now my requirements have changed. I want to enable/disable the button in the same way, but I want to EXCLUDE specific elements from this array. For example, I want to exclude these two items:
{id: ‘2’, name: ‘THIS_THING’, status: ‘AGREED’},
{id: ‘3’, name: ‘OTHER_THING’, status: ‘AGREED’},
Using my logic, how do I filter my array while EXCLUDING these two?
I realize I can just try to delete these specific items but I was wondering if there was potentially a cleaner way to do that without explicit deletion.
You can exclude a list of items by always assuming the given key/value pair is true regardless of the value
const exclusionList = ['THIS_THING', 'OTHER_THING'];
arr.filter((item) => item.indexOf(‘THING’) > -1)
.every((item) => item.status === ‘AGREED’ || exclusionList.includes(item.name))
Also please add your keyName(name) Where you using indexOf
let arr = [
{id: '1', name: 'SOME_THING', status: 'AGREED'},
{id: '2', name: 'THIS_THING', status: 'AGREED'},
{id: '3', name: 'OTHER_THING', status: 'AGREED'},
{id: '4', name: 'EVERY_THING', status: 'AGREED'},
];
const list = ['THIS_THING', 'OTHER_THING'];
console.log(arr.filter((item) => item.name.indexOf(‘THING’) > -1).every((user) => user.status === 'AGREED' || list.some(user.name)))

Removing duplicates from two arrays in javascript based on a single attribute

I have two arrays in javascript, where i want to do two different operations
Map an attribute upon each element in each lists
filter out none unique values based on an attribute
I have this function so far
export function generateDisplayedLabels(systemLabels, masterState){
const mappedSystemlabels = systemLabels.map(label => Object.assign(label, {type: "system"}))
const mappedMasterlabels = masterState.map(label => Object.assign(label, {type: "master"}))
const displayedLabels = _.union(mappedSystemlabels, mappedMasterState);
return displayedLabels
}
This would work except for the fact, that whenever i map over the objects in the beginning, the "unique" elements are no longer unique, because they have another attribute mapped upon it. Is there a time efficient way, that i can filter out the none unique elements, ignoring the attribute, that have been mapped onto it.
let ar1 = [
{
id: 1,
name: 'stack',
},
{
id: 2,
name: 'react',
},
];
let ar2 = [
{
id: 1,
name: 'javascript',
},
{
id: 2,
name: 'overflow',
},
{
id: 2,
name: 'react',
},
];
console.log(_.unionBy(ar1, ar2, 'id'));
console.log("======");
console.log(_.unionBy(ar1, ar2, 'name'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Ramda or ES6 - Return the value of an Object Property, after filtering an Array of Objects

I want to return one of the properties of an object, if the value of another property matches a constant.
Examples:
// The Array
[
{name: "Name1", description: "Name1 Description", enabled: true},
{name: "Name2", description: "Name2 Description", enabled: false},
{name: "Name3", description: "Name3 Description", enabled: false}
]
// The Constant
enum constant {
Name1 = 'Name1',
Name2 = 'Name2',
Name3 = 'Name3'
}
// What I want to return
// Value of enabled property, for the matching object
This is the code I wrote:
const filterBasedOnToggle = (featureTogglesArray: IFeatureToggle[], featureToggle: Toggle): boolean[] => {
return featureTogglesArray.filter((feature: IFeatureToggle) => feature.name === featureToggle).map(featureProperty => featureProperty.enabled);
};
This as you can see by the Typings, is returning an Array of Boolean value. I want to return the plain value. Any Ideas? Thank you!!
Didn't check if your code works, but as you said it returns a boolean array, so try using array.find to get the first match.
const MATCH = featureTogglesArray.find((feature: IFeatureToggle) => feature.name === featureToggle);
return MATCH === undefined ? false : MATCH.enabled;
Are you looking for something like this?
const filterBasedOnToggle = (toggles) => (searchName) => {
const feature = toggles .find (({name}) => name == searchName) || {}
return feature .enabled
}
const featureToggles = [{name: "Name1", description: "Name1 Description", enabled: true}, {name: "Name2", description: "Name2 Description", enabled: false}, {name: "Name3", description: "Name3 Description", enabled: false}];
const enabledByName = filterBasedOnToggle (featureToggles);
console .log (['Name1', 'Name2', 'Name3', 'Name4'] .map (enabledByName))
//~> [true, false, false, undefined]
This version uses find instead of filter to match on only the first one ... which was probably the signature issue with your version. It returns undefined if the value isn't matched, but you could easily make that false if you liked. You could of course write this with Ramda functions, but I don't see much there that would make this much simpler.

Javascript array difference

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!

How to prevent lodash mapKeys from reordering my array?

I'm using lodash mapKeys to take my array of objects and convert it to a mapped object using the id property. That's simple enough, but the problem is that it's sorting the new object by id.
For example if I had three objects in my array:
let myArray = [
{
id: 3,
name: 'Number Three'
},
{
id: 1,
name: 'Number One'
},
{
id: 2,
name: 'Number Two'
}
];
Then I map the keys by id:
_.mapKeys(myArray, 'id')
It returns the following:
{
1: {
id: 1,
name: 'Number One'
},
2: {
id: 2,
name: 'Number Two'
},
3: {
id: 3,
name: 'Number Three'
}
}
My server returns the array in a specific order, so I would like the objects to remain the same, so that when I loop over the object properties, they are in the correct order.
Is that possible with this method? If not, is there a possible alternative to achieve the results?
Use a Map because each item has a custom key (like objects), but the order of insertion will be the order of iteration (like arrays):
const myArray = [
{
id: 3,
name: 'Number Three'
},
{
id: 1,
name: 'Number One'
},
{
id: 2,
name: 'Number Two'
}
];
const map = myArray.reduce((map, item) => map.set(item.id, item), new Map());
map.forEach((item) => console.log(item));
As pointed out in the comments, looping over an object doesn't guarantee order. If you want an ordered list, you need an array.
However, you could apply the iterator pattern. In this pattern, it's up to you to decide what “next” element is. So, you could have a set with the objects (in order to get them in constant time) and an array to store the order. To iterate, you'd use the iterator.
This code could be used as example.
Hope it helps.
let myArray = [{
id: 3,
name: 'Number Three'
}, {
id: 1,
name: 'Number One'
}, {
id: 2,
name: 'Number Two'
}];
let myIterator = ((arr) => {
let mySet = _.mapKeys(arr, 'id'),
index = 0,
myOrder = _.map(arr, _.property('id'));
return {
getObjById: (id) => mySet[id],
next: () => mySet[myOrder[index++]],
hasNext: () => index < myOrder.length
};
})(myArray);
// Access elements by id in constant time.
console.log(myIterator.getObjById(1));
// Preserve the order that you got from your server.
while (myIterator.hasNext()) {
console.log(myIterator.next());
}
<script src="https://cdn.jsdelivr.net/lodash/4.16.6/lodash.min.js"></script>
Like mentioned in the comments, the best would be to keep the object references both in an array to keep the order and in a hash to ease updating.
Backbone's collection (source) works like this. It keeps objects in an array (models), but automatically updates a hash (_byId) when adding and removing models (objects) or when a model's id changes.
Here's a simple implementation of the concept. You could make your own implementation or check for a collection lib.
// a little setup
var array = [];
var hash = {};
var addObject = function addObject(obj) {
hash[obj.id] = obj;
array.push(obj);
}
// Create/insert the objects once
addObject({ id: 3, name: 'Number Three' });
addObject({ id: 1, name: 'Number One' });
addObject({ id: 2, name: 'Number Two' });
// Easy access by id
console.log("by id with hash", hash['1']);
// updating is persistent with the object in the array
hash['1'].name += " test";
// keeps the original ordering
for (var i = 0; i < 3; ++i) {
console.log("iterating", i, array[i]);
}

Categories