Related
Can this code be Improved? It arrange our array into sorted list(sortedArray) first and, the finalArray takes the final result;Example finalArray = [[1,1,1,1][2,2,2]...]
let array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20]
let sortedArray = []
let finalArray = []
sortedArray = array.sort((a, b) => {
if (a > b) return 1;
if (a < b) return -1;
return sortedArray.push(a - b);
});
finalArray = sortedArray.reduce((item, index) => {
if (typeof item.last === 'undefined' || item.last !== index) {
item.last = index;
item.sortedArray.push([]);
}
item.sortedArray[item.sortedArray.length - 1].push(index);
return item;
}, {
sortedArray: []
}).sortedArray;
console.log(finalArray);
Here is a shorter version than yours and more readable than Nina's
let array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20]
const finalArray = array.slice(0) // copy the array
.reduce((acc,cur) => {
const idx = acc.findIndex(item => item[0] === cur);
if (idx !=-1) acc[idx].push(cur); // just push
else acc.push([cur]); // push as array
return acc
},[])
.sort(([a],[b]) => a-b); // sort on the first entry
console.log(finalArray);
You could group first and then sort by the first item of each array.
If you have only positive 32 bit intger values, you could omit sorting, because the object is sorted like an array.
const
array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20],
result = Object
.values(array.reduce((r, v) => {
(r[v] ??= []).push(v);
return r;
}, {}))
.sort(([a], [b]) => a - b);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have the next situation in my react application:
I have a state:
const [arr1, setArr1] = useState([1, 2, 3, 5, 1, 3]);
Bellow i render all items from array on UI like:
arr1.map(i => <li>{i}</li>)
Now i want to remove all items that are equal in the array:
ex:
[1, 2, 3, 5, 1, 3] // should be deleted 1 and 3 result: [1, 2, 5]
[1, 2, 3, 5, 3] // should be deleted 3 result: [1, 2, 5]
Deleting all items also the state should change here arr1.map().
I tried setArr1([new Set(arr1)]), but it does not delete all duplicated values, it delete just one of them.
How to achieve what i described above?
You can count the frequency of number and then just pick the number whose frequency is 1.
const arr = [1, 2, 3, 5, 1, 3],
frequency = arr.reduce((o,v) => (o[v] = (o[v] || 0) + 1, o), {}),
unique = Object.keys(frequency).reduce((r,k) => frequency[k] === 1? [...r, +k]: r, []);
console.log(unique);
Check if the the first index equal to the last index on an element,when they are equals it means it is unique:
let result = []
let arr = [1, 2, 3, 5, 1, 3]
arr.forEach(e => arr.indexOf(e) === arr.lastIndexOf(e)?result.push(e):null)
console.log(result)
You can calculate the frequency and remove the number if the frequency is greater than 1. codesandbox
function removeDuplicates(arr) {
const frequency = {};
arr.forEach((el) => {
frequency[el] = frequency[el] ? ++frequency[el] : 1;
});
const result = [];
for (el in frequency) {
if (frequency[el] === 1) {
result.push(el);
}
}
return result;
}
const arrayWithoutDuplicates = removeDuplicates(arr1);
return (
<ul>
{arrayWithoutDuplicates.map((el) => {
return <li key={el}> {el} </li>;
})}
</ul>
);
Below is a one-liner using filter
const arr = [1, 2, 3, 5, 1, 3];
const unique = arr.filter(x => arr.filter(y => y === x).length < 2)
console.log(unique);
I have two arrays idarray and array and want to find item_tosearch in idarray. Then using the found index loop forward through idarray until an element is found that is not -1. Then use that index to retreive the value from array.
From what I know, if you want to keep checking you can use any sort of iteration either for or while or foreach in this case, I've got 2 arrays. First is for idarray and second is for array. I've managed to check what is the next data and if the data has reached the final value. I also able to get what I want which is the next data as long as the id wasn't -1.
What I've tried:
var item_tosearch = 0;
var idarray = [-1, 2, -1, 4, -1]
var array = [3, 2, 1, 0, 7];
var index = array.indexOf(item_tosearch);
if (index > -1) {
var res = array.slice(index);
}
if (res != undefined) {
for (let i = 0; i < res.length; i++) {
if (res[i + 1] != undefined) {
if (idarray[index + 1] == -1) {
if (res[i + 2] != undefined) {
console.log("Next = " + res[i + 2]);
break;
} else {
console.log("Final index");
break;
}
} else {
console.log("Next = " + res[i + 1]);
break;
}
} else {
console.log("Final index");
}
}
} else {
console.log('data not found');
}
My question is, is there any way I could've improved the method?
Any advice is apreciated.
Clarification:
If I have the following:
idarray = [-1, 2, -1, 4, 1];
array = [3, 2, 1, 0, 7];
What I would like to have is if I put 2 on item_tosearch as value, I'm expecting to have: 0 as the returned value since it was the next item without -1 in the id.
Another case, if I had:
idarray = [-1, 2, -1, -1, 1];
array = [3, 2, 1, 0, 7];
And if I put 2 on item_tosearch as value, I'm expecting to have: 7 as the returned value since it was the next item without -1 in the id.
But if idarray was = [-1, 2, -1, -1, -1] with the same 2 on item_tosearch as value. I expect "final index" to be returned. Since no more item without -1 as the id.
I've tried another iteration to fetch but doesn't seem to get what I want:
var item_tosearch = 2;
var idarray = [-1, 2, -1, -1, -1]
var array = [3, 2, 1, 0, 7];
var index = array.indexOf(item_tosearch);
if (index > -1) {
var res = array.slice(index);
}
if (res != undefined) {
for (let i = 0; i < res.length; i++) {
if (res[i + 1] != undefined) {
if (idarray[index + 1] == -1) {
for (let j = i + 1; j < res.length - i; j++) {
if (res[j + 1] != undefined) { // fetch if still got data with id != -1
console.log("Next = " + res[j + 1]); // should show next item without -1 in id
break;
} else {
console.log("Final index"); // reach end of array
break;
}
}
} else {
console.log("Next = " + res[i + 1]); // should show next item without -1 in id
break;
}
} else {
console.log("Final index"); // reach end of array
}
}
} else {
console.log('data not found');
}
If I understand your question good enough you're looking for something like this. Even if this isn't exactly the solution you want you might be able to get some inspiration from it.
This solution starts by finding the index of the element to search in idarray. If it can't be found return undefined.
Next start looping from 1 index higher until the end of the idarray. If an element is found that is not -1 return the element on the current index from array.
If nothing is found undefined is returned.
var idarray, array;
function giveThisABetterName(item_tosearch, idarray, array) {
var index = idarray.indexOf(item_tosearch);
if (index === -1) return; // data not found
for (index += 1; index < idarray.length; ++index) {
if (idarray[index] !== -1) return array[index];
}
// reach end of array
}
idarray = [-1, 2, -1, 4, 1];
array = [ 3, 2, 1, 0, 7];
console.log(giveThisABetterName(2, idarray, array));
idarray = [-1, 2, -1, -1, 1];
array = [ 3, 2, 1, 0, 7];
console.log(giveThisABetterName(2, idarray, array));
idarray = [-1, 2, -1, -1, 1];
array = [ 3, 2, 1, 0, 7];
console.log(giveThisABetterName(9, idarray, array));
Ok, I think I kind of understand the logic, but I'm not sure.
Is the question: I want to check if any of the ids following the id corresponding with my value, is not -1 ?
Hope I understood the logic correctly.
If you have no use for reusable functions, or don't care about structure, you can write this very short as well:
var pos = 0;
var idarray = [ -1, 2, -1, 4, -1 ];
var array = [ 3, 2, 1, 0, 7 ];
var get_result = ( array, idarray, pos, ex ) => {
const offset = array.indexOf( pos ) + 1;
return idarray
.slice( offset )
.reduce(( result, id, index ) => {
if ( result === "final index" && id !== -1 ) result = array[ index + offset ];
return result;
}, "final index" );
};
// example 1:
const ex1_search_value = 0; // pos
const ex1_ids = [ -1, 2, -1, 4, -1 ]; // idarray
const ex1_values = [3, 2, 1, 0, 7]; // array
// expect "final index", since our range will only contain the last id, which is -1
const result1 = get_result( ex1_values, ex1_ids, ex1_search_value );
console.log( `expect final index, ${ result1 }` );
// example2:
const ex2_search_value = 2;
const ex2_ids = [ -1, 2, -1, -1, -1 ];
const ex2_values = [3, 2, 1, 0, 7];
// expect "final index", since our range is the last two items, both with id -1
const result2 = get_result( ex2_values, ex2_ids, ex2_search_value );
console.log( `expect final index, ${ result2 }` );
// example3:
const ex3_search_value = 2;
const ex3_ids = [ -1, 2, -1, -1, -1, -1, -1, -1, -1, 3, -1, 2, -1, -1 ];
const ex3_values = [ 3, 2, 1, 0, 7, 4, 9, 14, 74, 8, 45, 14, 17, 84 ];
// expect { id: 3, value: 8 }
const result3 = get_result( ex3_values, ex3_ids, ex3_search_value );
console.log( `expect 8, ${ result3 }` );
// example4:
const ex4_search_value = 2;
const ex4_ids = [-1, 2, -1, 4, 1];
const ex4_values = [ 3, 2, 1, 0, 7];
// expect { id: 4, value: 0 }
const result4 = get_result( ex4_values, ex4_ids, ex4_search_value );
console.log( `expect 0, ${ result4 }` );
// example5:
const ex5_search_value = 2;
const ex5_ids = [-1, 2, -1, -1, 1];
const ex5_values = [ 3, 2, 1, 0, 7];
// expect { id: 1, value: 7 }
const result5 = get_result( ex5_values, ex5_ids, ex5_search_value );
console.log( `expect 7, ${ result5 }` );
// example6:
const ex6_search_value = 2;
const ex6_ids = [-1, 2, -1, -1, -1];
const ex6_values = [ 3, 2, 1, 0, 7];
// expect "final index"
const result6 = get_result( ex6_values, ex6_ids, ex6_search_value );
console.log( `expect final index, ${ result6 }` );
My other approach here is to merge the arrays into one array containing objects, so that we do not have to check for undefined values, while still being able to use array methods instead of plain loops. This would help if you have to use the id/value combinations alot in the code past this point. The functions are just there to make everything reusable.
// Create an object from the id and value combinations.
const create_collection = ( ids, values ) => {
return ids.map(( id, index ) => ({
id,
value: values[ index ]
}));
};
const has_valid_descendants = ( collection, search_value ) => {
// Find the index of the first item that has our requested value.
const search_index = collection.findIndex( item => item.value === search_value );
// Slice the relevant part from the collection.
// Since we will only look at records past the item ahving the search_value, we mights well only slice the relevant parts.
const collection_in_range = collection.slice( search_index + 1 );
// Find the first item in range has an id that is not -1.
return collection_in_range.find( item => item.id !== -1 ) || 'final index';
};
// example 1:
const ex1_search_value = 0; // pos
const ex1_ids = [ -1, 2, -1, 4, -1 ]; // idarray
const ex1_values = [3, 2, 1, 0, 7]; // array
// Collection should be: [{ id: -1, value: 3 },{ id: 2, value: 2 },{ id: -1, value: 1 },{ id: 4, value: 0 },{ id: -1, value: 7 }];
const ex1_collection = create_collection( ex1_ids, ex1_values );
console.log( ex1_collection );
// Is there a valid next item?
// expect "final index", since our range will only contain the last id, which is -1
const ex1_result = has_valid_descendants( ex1_collection, ex1_search_value );
console.log( 'expect 1: "final index"' );
console.log( `example 1: ${ JSON.stringify( ex1_result ) }` );
// example2:
const ex2_search_value = 2;
const ex2_ids = [ -1, 2, -1, -1, -1 ];
const ex2_values = [3, 2, 1, 0, 7];
// expect "final index", since our range is the last two items, both with id -1
const ex2_result = has_valid_descendants(
create_collection( ex2_ids, ex2_values ),
ex2_search_value
);
console.log( 'expect 2: "final index"' );
console.log( `example 2: ${ JSON.stringify( ex2_result ) }` );
// example3:
// We add a bunch of other values and ids.
// This proves it will work with longer arrays as well
// and that the result is the first item without the id -1
const ex3_search_value = 2;
const ex3_ids = [ -1, 2, -1, -1, -1, -1, -1, -1, -1, 3, -1, 2, -1, -1 ];
const ex3_values = [ 3, 2, 1, 0, 7, 4, 9, 14, 74, 8, 45, 14, 17, 84 ];
// expect { id: 3, value: 8 }
const ex3_result = has_valid_descendants(
create_collection( ex3_ids, ex3_values ),
ex3_search_value
);
console.log( 'expect 3: { id: 3, value: 8 }"' );
console.log( `example 3: ${ JSON.stringify( ex3_result ) }` );
// example4:
// Note: I've added || 'final index'; to the has_valid_descendants() function.
const ex4_search_value = 2;
const ex4_ids = [-1, 2, -1, 4, 1];
const ex4_values = [3, 2, 1, 0, 7];
// expect { id: 4, value: 0 }
const ex4_result = has_valid_descendants(
create_collection( ex4_ids, ex4_values ),
ex4_search_value
);
console.log( 'expect 4: { id: 4, value: 0 }' );
console.log( `example 4: ${ JSON.stringify( ex4_result ) }` );
// example5:
// Note: I've added || 'final index'; to the has_valid_descendants() function.
const ex5_search_value = 2;
const ex5_ids = [-1, 2, -1, -1, 1];
const ex5_values = [3, 2, 1, 0, 7];
// expect { id: 1, value: 7 }
const ex5_result = has_valid_descendants(
create_collection( ex5_ids, ex5_values ),
ex5_search_value
);
console.log( 'expect 5: { id: 1, value: 7 }' );
console.log( `example 5: ${ JSON.stringify( ex5_result ) }` );
// example6:
// Note: I've added || 'final index'; to the has_valid_descendants() function.
const ex6_search_value = 2;
const ex6_ids = [-1, 2, -1, -1, -1];
const ex6_values = [3, 2, 1, 0, 7];
// expect "final index"
const ex6_result = has_valid_descendants(
create_collection( ex6_ids, ex6_values ),
ex6_search_value
);
console.log( 'expect 6: "final index"' );
console.log( `example 6: ${ JSON.stringify( ex6_result ) }` );
Let's say I have an array such as: [1, 1, 2, 2, 3, 3, 4, 5]
And I want to remove this array of elements [1, 2, 3, 4, 5]
So in the end I want to be left with [1, 2, 3]
I have tried using the method below but it removes all copies of the elements from the main array.
myArray = myArray.filter( function( el ) {
return !toRemove.includes( el );
} );
Here is a way to do it using filter, indexOf and splice.
const input = [1, 1, 2, 2, 3, 3, 4, 5];
function removeSubset(arr, subset) {
const exclude = [...subset];
return arr.filter(x => {
const idx = exclude.indexOf(x);
if (idx >= 0) {
exclude.splice(idx, 1);
return false;
}
return true;
});
}
console.log(removeSubset(input, [1, 2, 3, 4, 5]));
You could get a Map and count the values and filter by checking the count and decrement the count if found.
var array = [1, 1, 2, 2, 3, 3, 4, 5],
remove = [1, 2, 3, 4, 5],
map = remove.reduce((m, v) => m.set(v, (m.get(v) || 0) + 1), new Map),
result = array.filter(v => !map.get(v) || !map.set(v, map.get(v) - 1));
console.log(result);
One solution is looping on the array of elements to remove and for each one remove the first element found on the input array:
const input = [1, 1, 2, 2, 3, 3, 4, 5];
const removeItems = (input, items) =>
{
// Make a copy, to not mutate the input.
let clonedInput = input.slice();
// Search and remove items.
items.forEach(x =>
{
let i = clonedInput.findIndex(y => y === x);
if (i >= 0) clonedInput.splice(i, 1);
});
return clonedInput;
}
console.log(removeItems(input, [1,2,3,4,5]));
console.log(removeItems(input, [1,2]));
console.log(removeItems(input, [1,99,44,5]));
If you still want to use filter, you can use the items to remove as the this argument of the filter, something like this:
const input = [1, 1, 2, 2, 3, 3, 4, 5];
const removeItems = (input, items) =>
{
return input.filter(function(x)
{
let i = this.findIndex(y => y === x);
return i >= 0 ? (this.splice(i, 1), false) : true;
}, items.slice());
}
console.log(removeItems(input, [1,2,3,4,5]));
console.log(removeItems(input, [1,2]));
console.log(removeItems(input, [1,99,44,5]));
You can use Filter and Shitf and Sort
let arr = [1, 1, 2, 2, 3, 3, 4, 5]
let remove = [1, 3, 2, 4, 5].sort((a,b)=>a-b)
let op = arr.sort((a,b)=>a-b).filter(e => ( remove.includes(e) ? (remove.shift(), false) : true ))
console.log(op)
What is the cleanest way to reduce those array ?
data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5, ...]
v: [10,10,10, 5, 10 ...]
}
For each id there is a v corresponding. What I want is sum up v for each id. In this example the result should be
data = {
id: [1, 3, 4, 5, ...]
v: [30, 15, ...]
}
I would go for the Array.prototype.reduce() ,simple and elegant solution
var ids = [1, 1, 1, 3, 3, 3, 3, 4, 5, 6, 6, 6],
v = [10, 10, 10, 5, 10, 10, 10, 404, 505, 600, 60, 6],
data = {};
data.v = [];
data.ids = ids.reduce(function(a, b, index) {
if (a.indexOf(b) < 0) a.push(b);
if (!data.v[a.indexOf(b)]) data.v[a.indexOf(b)] = 0;
data.v[a.indexOf(b)] += v[index];
return a;
}, []);
https://jsfiddle.net/2ssbngLr/
One way of doing this, given two arrays of equal length would be to map/reduce them:
const ids = [1, 1, 1, 3, 3];
const vs = [10,10,10,5,10];
const reduced = ids
.map((val, i) => ({ id: val, value: vs[i] }))
.reduce((agg, next) => {
agg[next.id] = (agg[next.id] || 0) + next.value;
return agg;
}, {});
console.log(reduced);
// Object {1: 30, 3: 15}
Working example: https://jsfiddle.net/h1o5rker/1/
I think it can be accomplished with reduce
var data = {
id: [1, 1, 1, 3, 3],
v: [10, 10, 10, 5, 10]
}
var sumsObjs = data.v.reduce(function(sum, val, index) {
var id = data.id[index];
if (sum[id] !== undefined) {
sum[id] = sum[id] + val;
} else {
sum[id] = val;
}
return sum;
}, {});
console.log(sumsObjs);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
var data={
id: [1,1,1,10,123,4531],
v:[123,123,53,223,11,11,11]
},
_v = data.v, vinit;
document.write(data.v+'<br>');
for(var i=0;i<_v.length;i++){
vinit = _v[i];
for(var j=i+1; j<=_v.length;j++){
if(_v[j]===vinit){
delete _v[j];
}
}
};
document.write(data.v);
var data={
id: [1,1,1,10,123,4531],
v:[123,123,53,223,11,11,11,...]
},
_v = data.v, vinit;
for(var i=0;i<_v.length;i++){
vinit = _v[i];
for(var j=i+1; j<=_v.length;j++){
if(_v[j]===vinit){
delete _v[j];
}
}
}
the above code is just for the v but you can simultaneously reduce the repeating elements for id too by introducing some more variables
in the snippet you can see that there are the extra commas in the second line which shows that those elements were deleted
If the ids are always in order, a simple for loop can solve it. There is no need to get overly complicated.
data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
v: [10, 10, 10, 5, 10, 1, 2, 3, 4]
};
var result = {
id: [],
v: []
};
(function() {
var ids = data.id,
vals = data.v,
lastId = ids[0],
runningTotal = vals[0];
for (var i = 1; i < ids.length; i++) {
if (lastId === ids[i]) {
runningTotal += vals[i];
}
if (lastId !== ids[i] || i + 1 === ids.length) {
result.id.push(lastId);
result.v.push(runningTotal);
lastId = ids[i];
runningTotal = vals[i];
}
}
}());
console.log(result);
Some people have posted some good solutions so far, but I haven't really seen one that does exactly what you're looking for. Here is one that takes your specific object and returns an object of the same format, but meeting your requirements and reduced.
// Your data object
data = {
id: [1, 1, 1, 3, 3],
v: [10,10,10, 5, 10]
}
// Assuming obj consists of `id` and `v`
function reduce(obj){
// We create our reduced object
var reducedObj = {
id: [],
v: []
}
// Next we create a hash map to store keys and values
var map = {};
for(var i=0; i<obj.id.length; ++i){
// If this key doesn't exist, create it and give it a value
if(typeof map[parseInt(obj.id[i])] === 'undefined'){
map[parseInt(obj.id[i])] = 0;
}
// Sum all of the values together for each key
map[parseInt(obj.id[i])] += parseInt(obj.v[i]);
}
// Now we map back our hashmap to our reduced object
for(var ele in map){
reducedObj.id.push(ele);
reducedObj.v.push(map[ele]);
}
// Return our new reduced object
return reducedObj;
}
var myReducedObject = reduce(data);
console.log(myReducedObject);
Working Fiddle
This is a solution for ordered id with Array.prototype.reduce().
var data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
v: [10, 10, 10, 5, 10, 7, 8, 10, 13]
},
result = { id: [], v: [] };
data.id.reduce(function (r, a, i) {
if (r === a) {
result.v[result.v.length - 1] += data.v[i];
} else {
result.id.push(a);
result.v.push(data.v[i]);
}
return a;
}, -1);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Or a in situ version
var data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
v: [10, 10, 10, 5, 10, 7, 8, 10, 13]
};
void function (d) {
var i = 1;
while (i < d.id.length) {
if (d.id[i - 1] === d.id[i]) {
d.id.splice(i, 1);
d.v[i - 1] += d.v.splice(i, 1)[0];
continue;
}
i++;
}
}(data);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');