Remove duplicate substring in an array - javascript

I have an array like below:
[
'author/2020/01/01/all_authors_000.csv',
'book/2020/01/01/all_books_000.csv',
'book/2020/01/01/all_books_001.csv',
'book/2020/01/01/all_books_002.csv',
'others/2020/01/01/other_stuff.csv',
]
As you can see there are three items that start with the word book. I want to remove all but one, so I end up with something like:
[
'author/2020/01/01/all_authors_000.csv',
'book/2020/01/01/all_books_000.csv',
'others/2020/01/01/other_stuff.csv',
]
How can I achieve this?

Here is working example:
var array = [
"author/2020/01/01/all_authors_000.csv",
"book/2020/01/01/all_books_000.csv",
"book/2020/01/01/all_books_001.csv",
"book/2020/01/01/all_books_002.csv",
"others/2020/01/01/other_stuff.csv",
];
var filteredArray = [];
var previous = "";
for (let i of array) {
if (i.substr(0, i.indexOf("/")) != previous) {
filteredArray.push(i);
previous = i.substr(0, i.indexOf("/"));
}
}
Every loop the value before "/2020" is stored inside the previous variable, and the if statement checks, if the value is the same as in the previous loop. If not, it pushes it into the filteredArray.
Therefore filteredArray is the array without duplicates.

Here is another method of doing it. Basically a function that takes in your array and a criteria to identify duplicates (in your case book). All of these duplicates will be removed but the first one.
const array = [
"author/2020/01/01/all_authors_000.csv",
"book/2020/01/01/all_books_000.csv",
"book/2020/01/01/all_books_001.csv",
"book/2020/01/01/all_books_002.csv",
"others/2020/01/01/other_stuff.csv",
];
const removeDuplicates = (array, criteria) => {
return array.filter(
(path) =>
![...array.filter((path) => path.includes(criteria)).splice(1)].includes(
path
)
);
};
console.log(removeDuplicates(array, "book"));

Related

How do I search array within an object for any instances of specified string values?

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.

Find Unique value from an array based on the array's string value (Javascript)

so I want to find unique values from an array.
so for example I have this array:
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
so I want to find the first matching value for each unique item.
for example, in the array, I have two strings with the shape prefix, six items with the size prefix, and two items with the height prefix.
so I want to output to be something like
const requiredVal = ["shape-10983", "size-2364", "height-3399"]
I want only the first value from any set of different values.
the simplest solution will be to iterate on the list and storing what you got in a dictionary
function removeSimilars(input) {
let values = {};
for (let value of input) {//iterate on the array
let key = value.splitOnLast('-')[0];//get the prefix
if (!(key in values))//if we haven't encounter the prefix yet
values[key] = value;//store that the first encounter with the prefix is with 'value'
}
return Object.values(values);//return all the values of the map 'values'
}
a shorter version will be this:
function removeSimilars(input) {
let values = {};
for (let value of input)
values[value.splitOnLast('-')[0]] ??= value;
return Object.values(values);
}
You could split the string and get the type and use it aks key for an object along with the original string as value. At result take only the values from the object.
const
data = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'],
result = Object.values(data.reduce((r, s) => {
const [type] = s.split('-', 1);
r[type] ??= s;
return r;
}, {}));
console.log(result);
If, as you mentioned in the comments, you have the list of prefixes already available, then all you have to do is iterate over those, to find each first element that starts with that prefix in your full list of possible values:
const prefixes = ['shape', 'size', 'height'];
const list = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
function reduceTheOptions(list = [], prefixes = [], uniques = []) {
prefixes.forEach(prefix =>
uniques.push(
list.find(e => e.startsWith(prefix))
)
);
return uniques;
}
console.log(reduceTheOptions(list, prefixes));
Try this:
function getRandomSet(arr, ...prefix)
{
// the final values are load into the array result variable
result = [];
const randomItem = (array) => array[Math.floor(Math.random() * array.length)];
prefix.forEach((pre) => {
result.push(randomItem(arr.filter((par) => String(par).startsWith(pre))));
});
return result;
}
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'];
console.log("Random values: ", getRandomSet(mainArr, "shape", "size", "height"));
I modified the #ofek 's answer a bit. cuz for some reason the ??= is not working in react project.
function removeSimilars(input) {
let values = {};
for (let value of input)
if (!values[value.split("-")[0]]) {
values[value.split("-")[0]] = value;
}
return Object.values(values);
}
create a new array and loop over the first array and check the existing of element before in each iteration if not push it to the new array

How to modify an array of array, depending if an element in it matches an element from another array of array?

I have 2 set of array of arrays in Google Spreadsheet as follows:-
var arrayInput = [[ASIAPLY, "9/10/2020"], [PCCS, "9/10/2020"], [SCGM, "9/10/2020"]]
var arrayOuput = [[PCCS, "8/10/2020"]]
I want to insert the 2nd index of an element/array in the arrayInput if the 1st index is present in the outputArray. If it is not, I would like to add the whole element/array into outputArray. My desired outcome would look like this
var arrayOuput = [[PCCS, "9/10/2020", "8/10/2020"], [ASIAPLY, "9/10/2020"], [SCGM, "9/10/2020"]]
I have tried this
function testData() {
// get the range of input data
var arrayInput = wlSheet.getRange(2, 2, 3, 2).getValues();
// get the range of output counter
var arrayOuput = wlSheet.getRange(2, 7, 1, 2).getValues();
arrayOuput.find((outputRow, i, arr) => {
arrayInput.map((r, index, array) => {
if (r[0] !== outputRow[0]) {
return wlSheet.getRange(arr.length + 2 + index, 7, 1, 2).setValues([[counter, hyperlinkText]]);
} else {
return wlSheet.getRange(i + 2, 8).insertCells(SpreadsheetApp.Dimension.COLUMNS).setValue(hyperlinkText);
}
});
});
}
However the code above has resulted into [[PCCS, "9/10/2020", "8/10/2020"], [PCCS, "9/10/2020"], [ASIAPLY, "9/10/2020"], [SCGM, "9/10/2020"]]; instead of desired result.
Is there a way of achieving what I intended to do in Google Apps Script?
Thanks in advance.
If you want to insert an item, you can't use Array.prototype.map as it will return a new array.
Now i'm not familiar with google apps scripts, or interacting with spreadsheets, but the basic JS would look like this:
What you are trying can be easily done with a for...of-loop
The basic steps are:
we have two arrays in the shape of a key followed by a number of values
if your familiar with TypeScript types: [key: Key, ...values: string[]]
we want to take from input to output
for each element in input
if output has an element with the corresponding key: append own values to it
else add self to output
const inArr = [['ASIAPLY', '9/10/2020'], ['PCCS', '9/10/2020'], ['SCGM', '9/10/2020']]
const outArr = [['PCCS', '8/10/2020']]
// iterate over the array elements and use destructuring to
// extract the key form the other values
for (const [key, ...values] of arrayInput) {
// look for an element in `output` that has that key
const target = outArr.find(([ky, value]) => ky === key)
// if we found one, push the values to it
if (target) target.push(...values)
// else push your key-values onto the output array
else outArr.push([key, ...values])
}
The result using the example arrays is:
[
[ 'PCCS', '8/10/2020', '9/10/2020' ],
[ 'ASIAPLY', '9/10/2020' ],
[ 'SCGM', '9/10/2020' ]
]
Since we used spread syntax (...values) in the destructuring, this is little iterator is able to handel 0 or more values by default and will always output the approptiate result.
This should solve this problem in an elegant manner and it is easyly modifiable if needed.
Create a new output array using Map and Array.map:
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
const array1 = [
['ASIAPLY', '9/10/2020'],
['PCCS', '9/10/2020'],
['SCGM', '9/10/2020'],
],
array2 = [['PCCS', '8/10/2020']],
array2Map = new Map(array2),
arrayOutput = array1.map(([k, v]) => [k, v, array2Map.get(k)]);
console.log(arrayOutput);
<!-- https://meta.stackoverflow.com/a/375985/ --> <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
To use setValues, the arrays must be of uniform size.

Javascript - Workaround for flatmap

So I was looking for some workaround for flat map as it doesn't work on IE and I find this one:
But I don't really understand why does it work
var gadjets = [
{computers:['asus', 'hp'],
sellphones:['Galaxy', 'Nokia']
},
{computers:['dell', 'insys'],
sellphones:['iphone', 'samsung']
}
];
const getValues = gadjets.reduce((acc, gadjet) => acc.concat(gadjet[computers]), []) // instead of gadjets.flatMap(gadjet=> gadjet[computers])
This code returns:
['asus','hp','dell','insys']
But shouldn't it return:
['asus','hp'],['dell', 'insys']
This is because reduce adds up the elements you give it. For example, take the following code:
let arr = [1,2,3,4,5];
console.log(arr.reduce((before, value)=>before+value));
This code takes each value and adds it to before. It then passes that added value into the next iteration of reduce, in the before variable.
In your code, you were passing an array into before, or in your case acc, and concatenates (merge) a new array from gadgets['computers'] and returns that array. This creates a list of the computers from the array of objects.
More info on reduce here.
But shouldn't it return
I'm not sure what you're trying to show us there, but if you mean
[['asus','hp'],['dell', 'insys']]
then no, it shouldn't. concat flattens arrays you pass it (to a single level):
const a = [].concat(['asus','hp'], ['dell', 'insys']);
console.log(a); // ["asus", "hp", "dell", "insys"]
So acc.concat(gadjet[computers]) flattens out each of those computers arrays into a new array, which is the accumulation result of the reduce.
In case you want the output to be array of arrays. Try this:
var gadjets = [
{ computers: ["asus", "hp"], sellphones: ["Galaxy", "Nokia"] },
{ computers: ["dell", "insys"], sellphones: ["iphone", "samsung"] }
];
const groupBy = key => {
let res = gadjets.reduce((objectsByKeyValue, obj) => {
let arr = [];
arr.push(obj[key]);
return objectsByKeyValue.concat(arr);
}, []);
return res;
};
console.log(groupBy("computers"));

How to compare two array objects' particular string?

I am little confused on how to compare two array objects' particular value get that neglected values as an array.
var Arr1 = [
{
jobId:"j1"
},
{
jobId:"j2"
},
{
jobId:"j3"
},
{
jobId:"j4"
},
]
var Arr2 = [
{
jobId:"j1"
},
{
jobId:"j2"
},
]
I want my result like this...
//neglected values
[
{
jobId:"j3"
},
{
jobId:"j4"
},
]
So let's think about how to go about comparing things in two arrays.
It probably makes sense to assume in order to fully compare both arrays, we need to iterate them, making sure we compare every item in each array so that we can be sure
So if I was begining i would think of nesting for - loops. Why? Because it will iterate over the first array, on each index it hits the first array, it will then iterate over every element in the second array.
then you can create some basic if conditional logic like if Arr1[i] === Arr2[j], push one of the objects into an array.
Filter (Array#filter) down the elements of the first array to those for which there are not (!) some (Array#some) matching job IDs in the second array.
Arr1.filter(arr1Elt => !Arr2.some(arr2Elt => arr2Elt.jobId === arr1Elt.jobId))
Using line-by-line comment style:
Arr1 // From Arr1,
.filter( // keep
arr1Elt => // elements for which
! // it is not the case that
Arr2 // Arr2 has
.some( // some
arr2Elt => // elements for which
arr2Elt.jobId // the job ID
=== // is equal to
arr1Elt.jobId // the job ID from the first array.
)
)
You can use lodash's _.differenceBy() to find the items from Arr1 missing from Arr2.
var Arr1 = [{"jobId":"j1"},{"jobId":"j2"},{"jobId":"j3"},{"jobId":"j4"}];
var Arr2 = [{"jobId":"j1"},{"jobId":"j2"}];
// find items that are in Arr1, but not in Arr2, compare items by their jobId
var result = _.differenceBy(Arr1, Arr2 , 'jobId');
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.3/lodash.min.js"></script>
You can use Array.prototype.filter on Arr1 and check if any of its entries are in Arr2 using Array.prototype.some - see demo below:
var Arr1=[{jobId:"j1"},{jobId:"j2"},{jobId:"j3"},{jobId:"j4"}];
var Arr2=[{jobId:"j1"},{jobId:"j2"}];
var result = Arr1.filter(function(e){
return !Arr2.some(function(k){
return k.jobId === e.jobId;
});
});
console.log(result);

Categories