How do I search an array for any instances of multiple specified string values?
const arrayOfObjects = [{
name: box1,
storage: ['car', 'goat', 'tea']
},
{
name: box2,
storage: ['camel', 'fox', 'tea']
}
];
arrayOfSearchItems = ['goat', 'car', 'oranges'];
If any one or all of the arrayOfSearchItems is present in one of the objects in my array, I want it to either return false or some other way that I can use to excluded that object that is in my arrayOfObjects from a new, filtered arrayOfObjects without any objects that contained the arrayOfSearchItems string values. In this case I would want an array of objects without box1.
Here is what I have tried to do, based on other suggestions. I spent a long time on this. The problem with this function is that it only works on the first arrayOfSearchItems strings, to exclude that object. It will ignore the second or third strings, and not exclude the object, even if it contains those strings. For example, it will exclude an object with 'goat'. Once that happens though, it will no longer exclude based on 'car'. I have tried to adapt my longer code for the purposes of this question, I may have some typos.
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => {
let incrementArray = [];
let userEffects = arrayOfSearchItems;
let objects = arrayOfObjects;
for (i = 0; i < userEffects.length; i++) {
for (x = 0; x < objects.length; x++) {
if (objects[x].storage.indexOf(userEffects) <= -1) {
incrementArray.push(objects[x]);
}
}
}
return(incrementArray);
}
let filteredArray = excludeItems(arrayOfSearchItems, arrayOfObjects);
console.log(filteredArray);
Thanks for providing some example code. That helps.
Let's start with your function, which has a good signature:
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => { ... }
If we describe what this function should do, we would say "it returns a new array of objects which do not contain any of the search items." This gives us a clue about how we should write our code.
Since we will be returning a filtered array of objects, we can start by using the filter method:
return arrayOfObjects.filter(obj => ...)
For each object, we want to make sure that its storage does not contain any of the search items. Another way to word this is "every item in the starage array does NOT appear in the list of search items". Now let's write that code using the every method:
.filter(obj => {
// ensure "every" storage item matches a condition
return obj.storage.every(storageItem => {
// the "condition" is that it is NOT in the array search items
return arrayOfSearchItems.includes(storageItem) === false);
});
});
Putting it all together:
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => {
return arrayOfObjects.filter(obj => {
return obj.storage.every(storageItem => {
return arrayOfSearchItems.includes(storageItem) === false;
});
});
}
Here's a fiddle: https://jsfiddle.net/3p95xzwe/
You can achieve your goal by using some of the built-in Array prototype functions, like filter, some and includes.
const excludeItems = (search, objs) =>
objs.filter(({storage:o}) => !search.some(s => o.includes(s)));
In other words: Filter my array objs, on the property storage to keep only those that they dont include any of the strings in search.
I have an simple javascript array that may contain duplicates or maynot contain duplicates.
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
I need to find the duplicate filepath and put their name into new array. If there is no duplicate then assign its name in first and then assign '' after two values. I could point all the codes that I have tried but it doesnt work. I accept jquery solution also. The expected outcome is this.
var outcome = [
[['aaa','sss','ppp'], 'pin/test1.html'],
[['bbb','eee','xxx'], 'pin/test2.html'],
[['ttt','ggg',''], 'test.html'],
[['yyy','',''], 'un/777.html'],
[['ggg','nnn',''], 'test3.html'],
];
What I have tried is this
for (var i = 0; i < arr.length; i++) {
var uniqueNames = [];
$.each(arr[i], function (i, el) {
if ($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
console.log(uniqueNames);
}
You could take a hash table and an array of empty strings and find the next slot for the value.
The array is reduced by taking an object as accumulator and a destructure array as value (the first part of the array) and key (the second part, aka filepath).
Inside of Array#reduce, a property check with the key is made and if undefined, an array with the wanted structure (array with two items, the first is an array with three emty spaces and the key) is being assigned by using a logical nullish assignment ??=.
The next line assigns the value to the next free slot, an item with an empty string.
Finally the accumulator is returned.
To get only an array as result, a conversion of the values of the object takes place.
let names = [['aaa','pin/test1.html'], ['bbb','pin/test2.html'], ['ttt','test.html'], ['ggg','test.html'], ['yyy','un/777.html'], ['ggg','test3.html'], ['nnn','test3.html'], ['eee','n/777.html'], ['sss','pin/test1.html'], ['xxx','pin/test2.html'], ['ppp','pin/test1.html']],
grouped = Object.values(names.reduce((r, [v, k]) => {
r[k] ??= [Array(3).fill(''), k];
r[k][0][r[k][0].indexOf('')] = v;
return r;
}, {}));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const aux = (names) => {
const hash = {};
let max = 0;
names.forEach(ele => {
if (!hash[ele[1]]) hash[ele[1]] = [];
hash[ele[1]].push(ele[0]);
max = Math.max(hash[ele[1]].length, max);
});
return Object.keys(hash).map(ele => [[...hash[ele], ...Array(max -hash[ele].length).fill("")], ele]);
}
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
console.log(aux(names))
This might help
You do not need jQuery for dealing with regular JS structure, you can achieve what you want with a simple code like this:
var names = [['aaa','pin/test1.html'],['bbb','pin/test2.html'],['ttt','test.html'],['ggg','test.html'],['yyy','un/777.html'],['ggg','test3.html'],['nnn','test3.html'],['eee','n/777.html'],['sss','pin/test1.html'],['xxx','pin/test2.html'],['ppp','pin/test1.html'],];
let lengthToFill = 0;
// collecting all the duplicates into a map
const pathMap = {};
names.forEach(name => {
// just in case if you're not familiar with array destructuring
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const [pathName, path] = name;
// make sure we have an array to deal with
// just in case you're not familiar with Nullish coalescing operator (??)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
pathMap[path] = pathMap[path] ?? [];
pathMap[path].push(pathName);
// tracking the max number of elements we're adding into a single entry
lengthToFill = Math.max(lengthToFill, pathMap[path].length);
});
const result = Object.entries(pathMap).map(entry => {
// constructing new array entry based on the data we've collected so far
return [
entry[1].concat(Array(lengthToFill - entry[1].length).fill('')),
entry[0],
];
});
console.log(result);
This solution will work for any number of elements that you'd like to fill the array with ''. It makes sure that the length of final listing is the same for all entries.
function countUniqueItems(arr) {
nums = [];
for (i = 0; i < arguments.length; i++) {
const item = arr[i];
console.log(i);
//console.log(item);
if (nums.includes(arr) === true) {
//console.log('8 is in the array');
//nums.push(arr)
} else {
nums.push(arr);
//console.log('8 is NOT in the array');
//nums.push(item)
}
}
return nums;
}
countUniqueItems(1, 2);
So it will give back the first argument which is 1 but i want it to be able to say argument 2 and 3 and so on
So you need to pass an array into the function, in place of 1,2 pass [1,2].
Then inside your function, you should use arr.length in place of arguments.length.
Then you look at your logic for the loop, you are pushing atm arr into nums, but if you pass and array that isn't really want you want, you should be pushing item as that is the variable which represents your current element from the array.
It looks from you comments like you're trying to make a unique list of inputs. Perhaps something like this would do the trick.
EDIT: Updated to use arguments
function uniqueNumbers() {
let arrayOfArguments = [...arguments]
let uniqueNums = [];
arrayOfArguments.map(i => !uniqueNums.includes(i) ? uniqueNums.push(i) : null);
return uniqueNums;
};
console.log(uniqueNumbers(1,2,3,3));
you should either pass an array to countUniqueItems or use the arguments keyword in the for-loop.
Your code is only seeing 1 (as arr inside the function).
basic implementation to find unique items
function countUniqueItems(...arr) {
let nums = [];
for (let num of arr) {
if (nums.indexOf(num) === -1) nums.push(num);
}
return nums;
}
console.log(countUniqueItems(1, 2, 1));
Using Set you can remove the duplicate values, you dont need to do logic run the loop to find the unique values from array.
const a = [1,2,3,3];
const uniqueSet = new Set(a);
uniqueSet.size
let uniqueFinder = arr => { const uniqueSet = new Set(arr); return uniqueSet.size}
const arrywithduplicates = [1,2,3,3,4,4];
uniqueFinder(arrywithduplicates) // return 4
Read more about Set : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
I have an array data, that contains a nodeData object, which contains an id
I make 2 copies of the array:
const dropAboveData = data.slice();
const dropBelowData = data.slice();
and then I try to modify both copies of my 2 copied arrays differently
for(let i = 0; i<data.length; i++){
dropAboveData[i].nodeData.id = -1;
dropBelowData[i].nodeData.id = -2;
}
So for example if each record in data had data[i].nodeData.id = 0, at the end i would expect dropAboveData to contain all -1 for id, and dropBelowData to contain all -2 for id.
But instead it seems like data, dropAboveData, and dropBelowData all become arrays filled with -2.
Why is this happening? I though slice() makes a copy of the array so that i'm not accessing the same object?
Slice makes a shallow copy
The slice() method returns a shallow copy of a portion of an array
into a new array object selected from begin to end (end not included).
The original array will not be modified.
You could copy an array of objects like this:
var data = [{
'a': '0'
}, {
'b': '1'
}, {
'c': '2'
}]
dropAboveData = []
dropBelowData = []
data.map(o => {
dropAboveData.push(JSON.parse(JSON.stringify(o)));
dropBelowData.push(JSON.parse(JSON.stringify(o)));
});
dropAboveData[0] = 1; //<-- modify only dropAboveData
dropAboveData[1].b = 99;//<-- modify only dropAboveData
console.log(dropAboveData)
console.log(dropBelowData)
as charieftl pointed out i was not copying the objects in my array (nodeData objects).
const dropAboveData = data.map(item => item.nodeData.id = -1);
const dropBelowData = data.map(item => item.nodeData.id = -2);
did what I wanted.
Not sure what I'm doing wrong.
function ccheck(){
var tkhContacts = SpreadsheetApp.openById('##').getSheetByName('contacts');
var emf = ContactsApp.getContactGroup('emf').getContacts();
var fullNames = emf.map( function(contact){ return contact.getFullName() } );
var tkhContacts = tkhContacts.getRange('B2:B').getValues();
for(var i=0;i<fullNames.length;i++){
if(fullNames[i].indexOf(tkhContacts) == -1){
Logger.log('missing')}
}
}
Trying to put all Google contacts in group 'emf' into an array. Then taking contact names stored in column B in sheet and putting that in an array. Then take each name in the 'fullNames' array and check if it matches any of the names in 'tkhContacts' from the sheet. If a name in 'fullNames' does not match any name in 'tkhContacts' set value as false.
I think that you are using the wrong indexOf method. It looks like you are using the String.prototype.indexOf() rather than Array.prototype.indexOf().
This should work for your code, but it is hard to test without any data.
const a = ['Sally', 'Walker', 'Claire', 'Lilly'];
const b = ['Kyle', 'Sally', 'Walker', 'Caroline', 'Claire'];
const d_hash = {};
const d_list = [];
a.forEach(a => {
const i = b.indexOf(a);
if (i === -1) {
// the name is missing
d_hash[a] = {
status: 'missing',
index: null
};
d_list.push(a);
} else {
// the name has been found
d_hash[a] = {
status: 'found',
index: i
}
}
});
console.log(d_hash);
console.log(d_list);
The logic:
I have two arrays of names, array a and array b. I want to find the names that appear in a but not in b.
For each value of a, try to find the index of the element in b. If the index is -1, we know the element could not be found.
Store the results as a hash and also the list of names that could not be found in an array.
JS Bin
Alternatively
What you are really wanting the do is find the difference of Set a and Set b.
A Set is a data structure that contains many elements and an element can only appear once it each set.
And a difference is also known as find the complement of a set.
We can convert each array to a set and then perform a difference to get the elements that appear in one but not the other.
const a = ['Sally', 'Walker', 'Claire', 'Lilly'];
const b = ['Kyle', 'Sally', 'Walker', 'Caroline', 'Claire'];
const set_a = new Set(a);
const set_b = new Set(b);
// code adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
Set.prototype.diff = function (b) {
const d = new Set(a);
b.forEach(el => d.delete(el));
return d;
}
console.log(Array.from(set_a.diff(set_b))); // ["Lilly"]
Clarifications:
What is forEach?
For each is a method natively provided on the Array.prototype in newer browsers. More info here.
forEach should be applied to an array, and the method expects a function callback that should handle each element.
What is (...) => { ... } ?
This represents arrow functions which are available in ES6. This arrow syntax provides an alternative (and in my opinion, cleaner and clearer) way to define functions.
Something that which was previously represented as:
function (el) {
d.delete(el);
}
Can be shortened to
(el) => d.delete(el);