I have a function that takes an array and string as arguments. The task is to chceck if the string occurs within the function and if does move to the first place of the array.
function moveToFirstPlace(['pizza', 'Pasta', 'Burger', 'PiZZa', 'pizzA'],'pizza')
in this example all pizzas have to be moved to the beggining of the array regardless of upper or lower letters.
You could sort the array and check the value and move this values to top.
function moveToFirstPlace(array, value) {
return array.sort((a, b) => (b.toLowerCase() === value) - (a.toLowerCase() === value));
}
console.log(moveToFirstPlace(['pizza', 'Pasta', 'Burger', 'PiZZa', 'pizzA'],'pizza'));
You can use push and unshift along with toLowerCase() to build a new array.
function moveToFirstPlace(items, key) {
let result = []
items.forEach(itm => {
itm.toLowerCase() == key.toLowerCase() ? result.unshift(itm) : result.push(itm)
})
return result
}
console.log(moveToFirstPlace(['pizza', 'Pasta', 'Burger', 'PiZZa', 'pizzA'], 'pizza'))
One way to do this is to apply filter to your array, once to identify the matches, and once for the non-matches. Then just concatenate both results:
function moveToFirstPlace(arr, str) {
str = str.toLowerCase();
const matches = arr.filter(elem => elem.toLowerCase() === str);
const misses = arr.filter(elem => elem.toLowerCase() !== str);
return matches.concat(misses);
}
var arr = moveToFirstPlace(['pizza', 'Pasta', 'Burger', 'PiZZa', 'pizzA'],'pizza');
console.log(arr);
This will maintain the original order of matching elements. Note that it does not mutate the original array, but returns a new array as result.
You could also use a more functional approach with reduce:
function moveToFirstPlace(arr, str) {
str = str.toLowerCase();
return arr.reduce((acc, elem) => (acc[+(elem.toLowerCase() === str)].push(elem), acc), [[], []])
.reduce((a, b) => a.concat(b));
}
var arr = moveToFirstPlace(['pizza', 'Pasta', 'Burger', 'PiZZa', 'pizzA'],'pizza');
console.log(arr);
Both solutions have a linear time complexity.
If efficiency is important, you can do this in-place with one O(n) pass (not counting toLowerCase) by running indexes from each end of the array and swapping when the right index finds pizza and the left finds non-pizza:
function moveToFirstPlace(arr, key){
let i = 0, j = arr.length -1
while (i < j){
while(!arr[j].toLowerCase().includes(key)) j--;
while(arr[i].toLowerCase().includes(key)) i++;
if (i < j)
[arr[j], arr[i]] = [arr[i], arr[j]];
i++;
j--;
}
}
let arr = ['pizza', 'Pasta', 'Burger', 'PiZZa', 'pizzA']
moveToFirstPlace(arr,'pizza')
console.log(arr)
Related
How to combine 2 arrays so that each of them has elements from the other for O (n) complexity?
I need that each object contain 1 element from another array, so we have 4 pair
let arr = [1,2]
let arr2 = ['first','second']
my solution with nested loops O(n^2)
const result = [];
for (let coachId of arr) {
for (let managerId of arr2)
result.push({ id1: coachId, id2: managerId });
}
expected result
[{id1: 1, id2: 'first'}, {id1: 1, id2: 'second'}, {id1: 2, id2: 'first'}, {id1: 2, id2: 'second'}]
As pointed out this won't reduce the time complexity of the full result, but you can use div & modulus to build up the result, this then avoids the double loop, and then you could use to reduce the time complexity of partial results, eg. paging data etc..
Out of curiosity did a quick benchmark doing this, and it is actually twice as fast too. But as pointed out in comments, it's more than likely the for of that's slows things down in your example, I assume there is a bit of overhead in JS creating the iterator for the array.
let arr = [1,2]
let arr2 = ['first','second']
const len = arr.length * arr2.length;
const result = new Array(len);
for (let p = 0; p < len; p += 1) {
result[p] = {
id1: arr[p / arr.length | 0],
id2: arr2[p % arr.length]
}
}
console.log(result);
You can take advantage of Array.prototype.reduce to alter the shape of the array
let arr = [1,2]
let arr2 = ['first','second']
const result = arr.reduce((accumulator, current) => {
const obj = arr2.map(item => {
return {id1: current, id2: item};
});
return accumulator.concat(obj);
}, []);
console.log(result);
Here is the process you start with performing a loop through the first array arr and for each item in that array you loop through each item in the second array arr2 and create an object with value from both Arrays
Here is what you could do in O(n) complexity, following would also handle case if your arrays aren't equal:
let arr = [1,2];
let arr2 = ['first','second','three'];
var res = [];
var maxLen = arr.length > arr2.length ? arr.length : arr2.length;
var i1=0;
var i2=0;
for(var i=0;i<maxLen;i++)
{
if(arr[i1])
{
res.push(arr[i1]);
i1++;
}
if(arr2[i2])
{
res.push(arr2[i2]);
i2++;
}
}
Suppose I have an array as below:
Arr1 = [12,30,30,60,11,12,30]
I need to find index of elements which are repeated in array e.g.
ans: 0,1,2,5,6
I've tried this code but it is considering just single element to check duplicates.
First get all the duplicates using filter() and then using reduce() get he indexes of only those elements of array which are in dups
const arr = [12,30,30,60,11,12,30];
const dups = arr.filter(x => arr.indexOf(x) !== arr.lastIndexOf(x));
const res = arr.reduce((ac, a, i) => {
if(dups.includes(a)){
ac.push(i)
}
return ac;
}, []);
console.log(res)
The time complexity of above algorithm is O(n^2). If you want O(n) you can use below way
const arr = [12,30,30,60,11,12,30];
const dups = arr.reduce((ac, a) => (ac[a] = (ac[a] || 0) + 1, ac), {})
const res = arr.reduce((ac, a, i) => {
if(dups[a] !== 1){
ac.push(i)
}
return ac;
}, []);
console.log(res)
You could use simple indexOf and the loop to get the duplicate indexes.
let arr = [12,30,30,60,11,12,30]
let duplicate = new Set();
for(let i = 0; i < arr.length; i++){
let index = arr.indexOf(arr[i], i + 1);
if(index != -1) {
duplicate.add(i);
duplicate.add(index);
}
}
console.log(Array.from(duplicate).sort().toString());
A slightly different approach with an object as closure for seen items which holds an array of index and the first array, in which later comes the index and a necessary flattening of the values.
This answer is based on the question how is it possible to insert a value into an already mapped value.
This is only possible by using an object reference which is saved at the moment where a value appears and which is not seen before.
Example of unfinished result
[
[0],
[1],
2,
[],
[],
5,
6
]
The final Array#flat removes the covering array and shows only the index, or nothing, if the array remains empty.
[0, 1, 2, 5, 6]
var array = [12, 30, 30, 60, 11, 12, 30],
indices = array
.map((o => (v, i) => {
if (o[v]) { // if is duplicate
o[v][1][0] = o[v][0]; // take the first index as well
return i; // return index
}
o[v] = [i, []]; // save index
return o[v][1]; // return empty array
})({}))
.flat() // remove [] and move values out of array
console.log(indices);
You could use Array#reduce method
loop the array with reduce.At the time find the index of argument
And check the arguments exist more than one in the array using Array#filter
Finaly push the index value to new accumulator array.If the index value already exist in accumalator.Then pass the currentIndex curInd of the array to accumulator
const arr = [12, 30, 30, 60, 11, 12, 30];
let res = arr.reduce((acc, b, curInd) => {
let ind = arr.indexOf(b);
if (arr.filter(k => k == b).length > 1) {
if (acc.indexOf(ind) > -1) {
acc.push(curInd)
} else {
acc.push(ind);
}
}
return acc;
}, []);
console.log(res)
Below code will be easiest way to find indexes of duplicate elements
var dupIndex = [];
$.each(Arr1, function(index, value){
if(Arr1.filter(a => a == value).length > 1){ dupIndex.push(index); }
});
This should work for you
I have this nested array
let arr = [['first', 'second'], ['third', 'fourth'], ['second', 'third']]
now I want to filter/remove based on exactly this array.
let filter = ['first', 'second']
and now my expected output should be:
[['third', 'fourth'], ['second', 'third']]
I only have this piece of code:
arr.filter(str => str.indexOf('second') === -1)
Which doesn't give the expected output, it also removed ['second', 'third'] because it filters whatever element that contains 'second'.. so they must be a better way or an improvement to the code.
If you care about ordering and need exact matches, you can write a simple arrays equal method and then filter out any equal arrays:
const arrEq = (a, b) => a.length === b.length && a.every((e, i) => b[i] === e);
const arr = [['first', 'second'], ['third', 'fourth'], ['second', 'third']];
const filter = ['first', 'second'];
console.log(arr.filter(e => !arrEq(e, filter)));
If you want the same elements but order doesn't matter:
const arrItemsEq = (a, b, cmp) => {
if (a.length !== b.length) {
return false;
}
a = a.slice().sort(cmp);
b = b.slice().sort(cmp);
return a.every((e, i) => e === b[i]);
};
const arr = [["a", "b"], ["b", "c"]];
const filter = ["b", "a"];
const strCmp = (x, y) => x.localeCompare(y);
console.log(arr.filter(e => !arrItemsEq(e, filter, strCmp)));
If you want to filter out arr elements if they don't include at least one of each filter element:
const arr = [["a", "b"], ["b", "c"]];
const filter = ["b", "a"];
console.log(arr.filter(x => !filter.every(y => x.includes(y))));
You need to test two arrays for equality. [There are many ways to do it] but once you pick one, you can simply remove any array that is equal to another. To avoid reimplementing the wheel, I'll use the LoDash _.isEqual for demonstration purposes:
let arr = [['first', 'second'], ['third', 'fourth']]
let filter = ['first', 'second']
let result = arr.filter(item => !_.isEqual(filter, item));
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
The equality function can be swapped to any implementation that satisfies you. A very simple one is simply:
function isEqual(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
but it's not guaranteed to be correct (e.g, isEqual([1, 2], ["1,2"]) //true) and it's going to be slow with large inputs. But it might still work, depending on circumstances.
You can use filter to check if the element doesn't include every string of the filter array.
let arr = [['first', 'second'], ['third', 'fourth'], ['second', 'third']]
let filterout = ['first', 'second']
let arr2 = arr.filter(x => ! filterout.every(y => x.includes(y)))
console.log(arr2)
But by using filter it basically creates a new array with fewer elements. Which is good enough for a small array.
If if the goal is to directly change the original array, then those elements can be spliced from that array.
let arr = [ ['first', 'second'], ['third', 'fourth'], ['second', 'third'], ['second', 'first'] ]
let filterout = ['first', 'second']
// getting the indexes of the element that need to be removed
let idxArr = []
arr.forEach( (x, idx) => { if(x.every(y => filterout.includes(y))) idxArr.push(idx)})
// removing the elements from the array
idxArr.sort((i,j)=>j-i).forEach(idx => {arr.splice(idx, 1)})
console.log(arr)
Suppose I have an array as such
var arr = [ [1,2,3],[4,5,1]]
How do I check if the columns are in ascending order?
For first column 1<4 returns true but the last column returns false as 3 is not less than 1.
The result should return an array of the columns that returns false
I would do it by first transposing the matrix (simply because its easier to deal with in js) and then map over each column (converting them from a set of numbers into a bool using Array#every).
const arr = [
[1,2,3],
[4,5,1]
];
const transpose = (arr) => Array(arr[0].length)
.fill(0)
.map((_, colIndex) => {
return Array(arr.length)
.fill(0)
.map((_, rowIndex) => {
return arr[rowIndex][colIndex];
});
});
const arr2 = transpose(arr);
const arr3 = arr2.map(col => {
let previousValue = -Infinity;
return col.every(v => {
const tmp = previousValue < v;
previousValue = v;
return tmp;
});
});
console.log(arr);
console.log(arr2);
console.log(arr3);
One possible solution is to use Array.map() over the first inner array, and then check if you found some element on a particular column that is not in order using Array.some():
var arr = [
[1, 2, 3],
[4, 5, 1],
[6, 7, 2]
];
let res = arr[0].map((n, cIdx) =>
{
return !arr.some((inner, rIdx) => rIdx > 0 && inner[cIdx] < arr[rIdx - 1][cIdx]);
});
console.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
To find the "columns" with the two values in descending order, you can use parallel arrays. Here is a succinct version of the code for that. (The second snippet is more verbose and provides further explanation.)
var arr = [ [1,2,3], [4,5,1] ], result = [];
arr[0].forEach((n, i) => { // n is the number, i is the index
if(arr[0][i] > arr[1][i]){ result.push(i); } // if descending, remember this index
});
console.log(result); // Logs `[2]` (an array with the index of the third column)
If you want to change which "column" the values are in so that they will be in ascending order, you could do it like this. When you find a "misplaced" value, store it in a temporary variable to make room. Then you can assign the two values to their "correct" locations.
const bigArr = [ [1,2,3], [4,5,1] ],
a1 = bigArr[0],
a2 = bigArr[1],
resultArr = [],
len = Math.min(a1.length, a2.length); // Uses min in case they have different lengths
for(let i = 0; i < len; i++){
if(a1[i] > a2[i]){ // Checks each position, and exchanges values if necessary
exchangeValuesInParallelArrays(a1, a2, i);
resultArr.push(i); // Adds this index to the results array
}
}
console.log(bigArr); // Logs `[ [1,2,1], [4,5,3] ]`
console.log(resultArr) // Logs `[2]`
function exchangeValuesInParallelArrays(arr1, arr2, index){
let tempStorage = arr1[index];
arr1[index] = arr2[index];
arr2[index] = tempStorage;
}
I'm sure there are many ways to achieve that but I'm looking for something "elegant".
a = [
'a',
'b',
'c'
];
magicArrayJoin(a, {value: 255} ); // insert the same object between each item
result == [
'a',
{value: 255},
'b',
{value: 255}
'c'
];
All proposals are welcome. :)
One-liner using plain ES6:
const interleave = (arr, thing) => [].concat(...arr.map(n => [n, thing])).slice(0, -1)
Usage:
interleave(['foo', 'bar', 'baz'], 'avocado')
Prints:
> ["foo", "avocado", "bar", "avocado", "baz"]
You can do it with flatMap. It can be found from lodash for example
_.flatMap([1,2,3,4], (value, index, array) =>
array.length -1 !== index // check for the last item
? [value, "s"]
: value
);
ouputs
[1, "s", 2, "s", 3, "s", 4]
Update
Array#flatMap proposal is in the works so in future this should work:
[1, 2, 3, 4].flatMap(
(value, index, array) =>
array.length - 1 !== index // check for the last item
? [value, "s"]
: value,
);
In my opinion the most elegant way to do this is the following one:
ES6 syntax version
const insertIntoArray = (arr, value) => {
return arr.reduce((result, element, index, array) => {
result.push(element);
if (index < array.length - 1) {
result.push(value);
}
return result;
}, []);
};
Usage:
insertIntoArray([1, 2, 3], 'x'); // => [1, 'x', 2, 'x', 3]
An ordinary loop seems to be the best:
function intersperse(arr, el) {
var res = [], i=0;
if (i < arr.length)
res.push(arr[i++]);
while (i < arr.length)
res.push(el, arr[i++]);
return res;
}
If you're looking for something elegant, it would probably have to use some kind of concatMap, as in
function concatMap(arr, fn) { return [].concat.apply([], arr.map(fn)); }
function intersperse(arr, el) { return concatMap(arr, x => [el, x]).slice(1); }
Use ES6 flatMap function.
const insertBetween = (ele, array) => {
return array.flatMap((x) => [ele, x]).slice(1);
};
insertBetween('+', [1, 2, 3]);
Immutable solution
When reducing an array the reduce function should not mutate the array but return a new value (in this case a new array). That way the changes will be only applied to the returned array and not the original one and side effects will be avoided.
const insertBetween = (insertee, array) => array.reduce(
(acc, item, i, { length }) => {
if (i && i < length) {
return [...acc, insertee, item];
}
return [...acc, item];
},
[]
);
Ramda has intersperse method that:
Creates a new list with the separator interposed between elements.
Code:
R.intersperse({name: 'separator'}, ['one', 'two', 'three']);
Result:
[
'one',
{name: 'separator'},
'two',
{name: 'separator'},
'three'
]
This worked for me:
a.map(val = [val, {value: 255}]).flat()
You can achieve this using reduce (it is also immutable).
const insertBetween = (insertion, array) =>
array.reduce(
(newArray, member, i, array) =>
i < array.length - 1
? newArray.concat(member, insertion)
: newArray.concat(member),
[]
);
const result = insertBetween('and', [1, 2, 3]);
console.log(result);
// outputs;
// [
// 1,
// 'and',
// 2,
// 'and',
// 3
// ]
Or in older JS syntax;
function insertBetween(insertion, array) {
const indexOfLastItem = array.length - 1;
return array.reduce(withInsertion, []);
function withInsertion(newArray, item, index, array) {
return index < indexOfLastItem
? newArray.concat(item, insertion)
: newArray.concat(item);
}
}
const result = insertBetween('and', [1, 2, 3]);
console.log(result);
// outputs;
// [
// 1,
// 'and',
// 2,
// 'and',
// 3
// ]
I really in favor of #Vidul 's comments, which is very logical and concise! I myself also came up with splice(), but missed %. However, most of the braces seem unnecessary as an oneliner. It can be further simplified as
for (var i = 0; i < a.length; i++) if (i % 2) a.splice(i, 0, {value: 255});
function insertObject(arr, obj) {
var result = [];
function insert(element, index) {
result.push(element);
if (index + 1 < arr.length) {
result.push(obj);
}
}
arr.forEach(insert);
return result;
}
var a = [1, 2, 3, 4];
insertObject(a, {
test: 'test'
});
Using splice as Kamen suggests, you could do something like:
const numSeparators = arr.length - 1;
for (let i = 1; i <= numSeparators; i++) {
const index = (i * 2) - 1;
arr.splice(index, 0, { value: 255 });
}
for a simple purely functional way I suggest doing it this way:
const magicArrayJoin = (array, el) =>
array.length ?
array.slice(1).reduce((acc, cur) => acc.concat([el, cur]), [array[0]]) :
[]
p.n. this way is not the most performant one in javascript
ES6:
const arrayWithSeparator = array.reduce((a, i) => a.length ? a.push(separator) && a.push(i) && a : a.push(u) && a, [])
Array.splice() should do the job like so:
a.splice(1, 0, {value : 255})
The first argument is the position at which you want to delete or insert elements, the second is the delete count, the third (optional) is the new element[s] you want to insert.